Fix #5681 - fix bootstrap4 detection
[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, 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 (return false to block)
2064          * @param {Roo.menu.Menu} this
2065          */
2066         beforeshow : true,
2067         /**
2068          * @event beforehide
2069          * Fires before this menu is hidden (return false to block)
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     {
2293         if (false === this.fireEvent("beforeshow", this)) {
2294             Roo.log("show canceled");
2295             return;
2296         }
2297         this.parentMenu = parentMenu;
2298         if(!this.el){
2299             this.render();
2300         }
2301         
2302         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2303     },
2304      /**
2305      * Displays this menu at a specific xy position
2306      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2307      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2308      */
2309     showAt : function(xy, parentMenu, /* private: */_e){
2310         this.parentMenu = parentMenu;
2311         if(!this.el){
2312             this.render();
2313         }
2314         if(_e !== false){
2315             this.fireEvent("beforeshow", this);
2316             //xy = this.el.adjustForConstraints(xy);
2317         }
2318         
2319         //this.el.show();
2320         this.hideMenuItems();
2321         this.hidden = false;
2322         this.triggerEl.addClass('open');
2323         this.el.addClass('show');
2324         
2325         // reassign x when hitting right
2326         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2327             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2328         }
2329         
2330         // reassign y when hitting bottom
2331         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2332             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2333         }
2334         
2335         // but the list may align on trigger left or trigger top... should it be a properity?
2336         
2337         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2338             this.el.setXY(xy);
2339         }
2340         
2341         this.focus();
2342         this.fireEvent("show", this);
2343     },
2344     
2345     focus : function(){
2346         return;
2347         if(!this.hidden){
2348             this.doFocus.defer(50, this);
2349         }
2350     },
2351
2352     doFocus : function(){
2353         if(!this.hidden){
2354             this.focusEl.focus();
2355         }
2356     },
2357
2358     /**
2359      * Hides this menu and optionally all parent menus
2360      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2361      */
2362     hide : function(deep)
2363     {
2364         if (false === this.fireEvent("beforehide", this)) {
2365             Roo.log("hide canceled");
2366             return;
2367         }
2368         this.hideMenuItems();
2369         if(this.el && this.isVisible()){
2370            
2371             if(this.activeItem){
2372                 this.activeItem.deactivate();
2373                 this.activeItem = null;
2374             }
2375             this.triggerEl.removeClass('open');;
2376             this.el.removeClass('show');
2377             this.hidden = true;
2378             this.fireEvent("hide", this);
2379         }
2380         if(deep === true && this.parentMenu){
2381             this.parentMenu.hide(true);
2382         }
2383     },
2384     
2385     onTriggerClick : function(e)
2386     {
2387         Roo.log('trigger click');
2388         
2389         var target = e.getTarget();
2390         
2391         Roo.log(target.nodeName.toLowerCase());
2392         
2393         if(target.nodeName.toLowerCase() === 'i'){
2394             e.preventDefault();
2395         }
2396         
2397     },
2398     
2399     onTriggerPress  : function(e)
2400     {
2401         Roo.log('trigger press');
2402         //Roo.log(e.getTarget());
2403        // Roo.log(this.triggerEl.dom);
2404        
2405         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2406         var pel = Roo.get(e.getTarget());
2407         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2408             Roo.log('is treeview or dropdown?');
2409             return;
2410         }
2411         
2412         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2413             return;
2414         }
2415         
2416         if (this.isVisible()) {
2417             Roo.log('hide');
2418             this.hide();
2419         } else {
2420             Roo.log('show');
2421             this.show(this.triggerEl, '?', false);
2422         }
2423         
2424         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2425             e.stopEvent();
2426         }
2427         
2428     },
2429        
2430     
2431     hideMenuItems : function()
2432     {
2433         Roo.log("hide Menu Items");
2434         if (!this.el) { 
2435             return;
2436         }
2437         
2438         this.el.select('.open',true).each(function(aa) {
2439             
2440             aa.removeClass('open');
2441          
2442         });
2443     },
2444     addxtypeChild : function (tree, cntr) {
2445         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2446           
2447         this.menuitems.add(comp);
2448         return comp;
2449
2450     },
2451     getEl : function()
2452     {
2453         Roo.log(this.el);
2454         return this.el;
2455     },
2456     
2457     clear : function()
2458     {
2459         this.getEl().dom.innerHTML = '';
2460         this.menuitems.clear();
2461     }
2462 });
2463
2464  
2465  /*
2466  * - LGPL
2467  *
2468  * menu item
2469  * 
2470  */
2471
2472
2473 /**
2474  * @class Roo.bootstrap.MenuItem
2475  * @extends Roo.bootstrap.Component
2476  * Bootstrap MenuItem class
2477  * @cfg {String} html the menu label
2478  * @cfg {String} href the link
2479  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2480  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2481  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2482  * @cfg {String} fa favicon to show on left of menu item.
2483  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2484  * 
2485  * 
2486  * @constructor
2487  * Create a new MenuItem
2488  * @param {Object} config The config object
2489  */
2490
2491
2492 Roo.bootstrap.MenuItem = function(config){
2493     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2494     this.addEvents({
2495         // raw events
2496         /**
2497          * @event click
2498          * The raw click event for the entire grid.
2499          * @param {Roo.bootstrap.MenuItem} this
2500          * @param {Roo.EventObject} e
2501          */
2502         "click" : true
2503     });
2504 };
2505
2506 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2507     
2508     href : false,
2509     html : false,
2510     preventDefault: false,
2511     isContainer : false,
2512     active : false,
2513     fa: false,
2514     
2515     getAutoCreate : function(){
2516         
2517         if(this.isContainer){
2518             return {
2519                 tag: 'li',
2520                 cls: 'dropdown-menu-item '
2521             };
2522         }
2523         var ctag = {
2524             tag: 'span',
2525             html: 'Link'
2526         };
2527         
2528         var anc = {
2529             tag : 'a',
2530             cls : 'dropdown-item',
2531             href : '#',
2532             cn : [  ]
2533         };
2534         
2535         if (this.fa !== false) {
2536             anc.cn.push({
2537                 tag : 'i',
2538                 cls : 'fa fa-' + this.fa
2539             });
2540         }
2541         
2542         anc.cn.push(ctag);
2543         
2544         
2545         var cfg= {
2546             tag: 'li',
2547             cls: 'dropdown-menu-item',
2548             cn: [ anc ]
2549         };
2550         if (this.parent().type == 'treeview') {
2551             cfg.cls = 'treeview-menu';
2552         }
2553         if (this.active) {
2554             cfg.cls += ' active';
2555         }
2556         
2557         
2558         
2559         anc.href = this.href || cfg.cn[0].href ;
2560         ctag.html = this.html || cfg.cn[0].html ;
2561         return cfg;
2562     },
2563     
2564     initEvents: function()
2565     {
2566         if (this.parent().type == 'treeview') {
2567             this.el.select('a').on('click', this.onClick, this);
2568         }
2569         
2570         if (this.menu) {
2571             this.menu.parentType = this.xtype;
2572             this.menu.triggerEl = this.el;
2573             this.menu = this.addxtype(Roo.apply({}, this.menu));
2574         }
2575         
2576     },
2577     onClick : function(e)
2578     {
2579         Roo.log('item on click ');
2580         
2581         if(this.preventDefault){
2582             e.preventDefault();
2583         }
2584         //this.parent().hideMenuItems();
2585         
2586         this.fireEvent('click', this, e);
2587     },
2588     getEl : function()
2589     {
2590         return this.el;
2591     } 
2592 });
2593
2594  
2595
2596  /*
2597  * - LGPL
2598  *
2599  * menu separator
2600  * 
2601  */
2602
2603
2604 /**
2605  * @class Roo.bootstrap.MenuSeparator
2606  * @extends Roo.bootstrap.Component
2607  * Bootstrap MenuSeparator class
2608  * 
2609  * @constructor
2610  * Create a new MenuItem
2611  * @param {Object} config The config object
2612  */
2613
2614
2615 Roo.bootstrap.MenuSeparator = function(config){
2616     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2617 };
2618
2619 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2620     
2621     getAutoCreate : function(){
2622         var cfg = {
2623             cls: 'divider',
2624             tag : 'li'
2625         };
2626         
2627         return cfg;
2628     }
2629    
2630 });
2631
2632  
2633
2634  
2635 /*
2636 * Licence: LGPL
2637 */
2638
2639 /**
2640  * @class Roo.bootstrap.Modal
2641  * @extends Roo.bootstrap.Component
2642  * Bootstrap Modal class
2643  * @cfg {String} title Title of dialog
2644  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2645  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2646  * @cfg {Boolean} specificTitle default false
2647  * @cfg {Array} buttons Array of buttons or standard button set..
2648  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2649  * @cfg {Boolean} animate default true
2650  * @cfg {Boolean} allow_close default true
2651  * @cfg {Boolean} fitwindow default false
2652  * @cfg {String} size (sm|lg) default empty
2653  * @cfg {Number} max_width set the max width of modal
2654  *
2655  *
2656  * @constructor
2657  * Create a new Modal Dialog
2658  * @param {Object} config The config object
2659  */
2660
2661 Roo.bootstrap.Modal = function(config){
2662     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2663     this.addEvents({
2664         // raw events
2665         /**
2666          * @event btnclick
2667          * The raw btnclick event for the button
2668          * @param {Roo.EventObject} e
2669          */
2670         "btnclick" : true,
2671         /**
2672          * @event resize
2673          * Fire when dialog resize
2674          * @param {Roo.bootstrap.Modal} this
2675          * @param {Roo.EventObject} e
2676          */
2677         "resize" : true
2678     });
2679     this.buttons = this.buttons || [];
2680
2681     if (this.tmpl) {
2682         this.tmpl = Roo.factory(this.tmpl);
2683     }
2684
2685 };
2686
2687 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2688
2689     title : 'test dialog',
2690
2691     buttons : false,
2692
2693     // set on load...
2694
2695     html: false,
2696
2697     tmp: false,
2698
2699     specificTitle: false,
2700
2701     buttonPosition: 'right',
2702
2703     allow_close : true,
2704
2705     animate : true,
2706
2707     fitwindow: false,
2708     
2709      // private
2710     dialogEl: false,
2711     bodyEl:  false,
2712     footerEl:  false,
2713     titleEl:  false,
2714     closeEl:  false,
2715
2716     size: '',
2717     
2718     max_width: 0,
2719     
2720     max_height: 0,
2721     
2722     fit_content: false,
2723
2724     onRender : function(ct, position)
2725     {
2726         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2727
2728         if(!this.el){
2729             var cfg = Roo.apply({},  this.getAutoCreate());
2730             cfg.id = Roo.id();
2731             //if(!cfg.name){
2732             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2733             //}
2734             //if (!cfg.name.length) {
2735             //    delete cfg.name;
2736            // }
2737             if (this.cls) {
2738                 cfg.cls += ' ' + this.cls;
2739             }
2740             if (this.style) {
2741                 cfg.style = this.style;
2742             }
2743             this.el = Roo.get(document.body).createChild(cfg, position);
2744         }
2745         //var type = this.el.dom.type;
2746
2747
2748         if(this.tabIndex !== undefined){
2749             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2750         }
2751
2752         this.dialogEl = this.el.select('.modal-dialog',true).first();
2753         this.bodyEl = this.el.select('.modal-body',true).first();
2754         this.closeEl = this.el.select('.modal-header .close', true).first();
2755         this.headerEl = this.el.select('.modal-header',true).first();
2756         this.titleEl = this.el.select('.modal-title',true).first();
2757         this.footerEl = this.el.select('.modal-footer',true).first();
2758
2759         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2760         
2761         //this.el.addClass("x-dlg-modal");
2762
2763         if (this.buttons.length) {
2764             Roo.each(this.buttons, function(bb) {
2765                 var b = Roo.apply({}, bb);
2766                 b.xns = b.xns || Roo.bootstrap;
2767                 b.xtype = b.xtype || 'Button';
2768                 if (typeof(b.listeners) == 'undefined') {
2769                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2770                 }
2771
2772                 var btn = Roo.factory(b);
2773
2774                 btn.render(this.getButtonContainer());
2775
2776             },this);
2777         }
2778         // render the children.
2779         var nitems = [];
2780
2781         if(typeof(this.items) != 'undefined'){
2782             var items = this.items;
2783             delete this.items;
2784
2785             for(var i =0;i < items.length;i++) {
2786                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2787             }
2788         }
2789
2790         this.items = nitems;
2791
2792         // where are these used - they used to be body/close/footer
2793
2794
2795         this.initEvents();
2796         //this.el.addClass([this.fieldClass, this.cls]);
2797
2798     },
2799
2800     getAutoCreate : function()
2801     {
2802         var bdy = {
2803                 cls : 'modal-body',
2804                 html : this.html || ''
2805         };
2806
2807         var title = {
2808             tag: 'h4',
2809             cls : 'modal-title',
2810             html : this.title
2811         };
2812
2813         if(this.specificTitle){
2814             title = this.title;
2815
2816         }
2817
2818         var header = [];
2819         if (this.allow_close && Roo.bootstrap.version == 3) {
2820             header.push({
2821                 tag: 'button',
2822                 cls : 'close',
2823                 html : '&times'
2824             });
2825         }
2826
2827         header.push(title);
2828
2829         if (this.allow_close && Roo.bootstrap.version == 4) {
2830             header.push({
2831                 tag: 'button',
2832                 cls : 'close',
2833                 html : '&times'
2834             });
2835         }
2836         
2837         var size = '';
2838
2839         if(this.size.length){
2840             size = 'modal-' + this.size;
2841         }
2842         
2843         var footer = Roo.bootstrap.version == 3 ?
2844             {
2845                 cls : 'modal-footer',
2846                 cn : [
2847                     {
2848                         tag: 'div',
2849                         cls: 'btn-' + this.buttonPosition
2850                     }
2851                 ]
2852
2853             } :
2854             {  // BS4 uses mr-auto on left buttons....
2855                 cls : 'modal-footer'
2856             };
2857
2858             
2859
2860         
2861         
2862         var modal = {
2863             cls: "modal",
2864              cn : [
2865                 {
2866                     cls: "modal-dialog " + size,
2867                     cn : [
2868                         {
2869                             cls : "modal-content",
2870                             cn : [
2871                                 {
2872                                     cls : 'modal-header',
2873                                     cn : header
2874                                 },
2875                                 bdy,
2876                                 footer
2877                             ]
2878
2879                         }
2880                     ]
2881
2882                 }
2883             ]
2884         };
2885
2886         if(this.animate){
2887             modal.cls += ' fade';
2888         }
2889
2890         return modal;
2891
2892     },
2893     getChildContainer : function() {
2894
2895          return this.bodyEl;
2896
2897     },
2898     getButtonContainer : function() {
2899         
2900          return Roo.bootstrap.version == 4 ?
2901             this.el.select('.modal-footer',true).first()
2902             : this.el.select('.modal-footer div',true).first();
2903
2904     },
2905     initEvents : function()
2906     {
2907         if (this.allow_close) {
2908             this.closeEl.on('click', this.hide, this);
2909         }
2910         Roo.EventManager.onWindowResize(this.resize, this, true);
2911
2912
2913     },
2914   
2915
2916     resize : function()
2917     {
2918         this.maskEl.setSize(
2919             Roo.lib.Dom.getViewWidth(true),
2920             Roo.lib.Dom.getViewHeight(true)
2921         );
2922         
2923         if (this.fitwindow) {
2924             
2925            
2926             this.setSize(
2927                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2928                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
2929             );
2930             return;
2931         }
2932         
2933         if(this.max_width !== 0) {
2934             
2935             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2936             
2937             if(this.height) {
2938                 this.setSize(w, this.height);
2939                 return;
2940             }
2941             
2942             if(this.max_height) {
2943                 this.setSize(w,Math.min(
2944                     this.max_height,
2945                     Roo.lib.Dom.getViewportHeight(true) - 60
2946                 ));
2947                 
2948                 return;
2949             }
2950             
2951             if(!this.fit_content) {
2952                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2953                 return;
2954             }
2955             
2956             this.setSize(w, Math.min(
2957                 60 +
2958                 this.headerEl.getHeight() + 
2959                 this.footerEl.getHeight() + 
2960                 this.getChildHeight(this.bodyEl.dom.childNodes),
2961                 Roo.lib.Dom.getViewportHeight(true) - 60)
2962             );
2963         }
2964         
2965     },
2966
2967     setSize : function(w,h)
2968     {
2969         if (!w && !h) {
2970             return;
2971         }
2972         
2973         this.resizeTo(w,h);
2974     },
2975
2976     show : function() {
2977
2978         if (!this.rendered) {
2979             this.render();
2980         }
2981
2982         //this.el.setStyle('display', 'block');
2983         this.el.removeClass('hideing');
2984         this.el.dom.style.display='block';
2985         
2986         Roo.get(document.body).addClass('modal-open');
2987  
2988         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2989             
2990             (function(){
2991                 this.el.addClass('show');
2992                 this.el.addClass('in');
2993             }).defer(50, this);
2994         }else{
2995             this.el.addClass('show');
2996             this.el.addClass('in');
2997         }
2998
2999         // not sure how we can show data in here..
3000         //if (this.tmpl) {
3001         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3002         //}
3003
3004         Roo.get(document.body).addClass("x-body-masked");
3005         
3006         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3007         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3008         this.maskEl.dom.style.display = 'block';
3009         this.maskEl.addClass('show');
3010         
3011         
3012         this.resize();
3013         
3014         this.fireEvent('show', this);
3015
3016         // set zindex here - otherwise it appears to be ignored...
3017         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3018
3019         (function () {
3020             this.items.forEach( function(e) {
3021                 e.layout ? e.layout() : false;
3022
3023             });
3024         }).defer(100,this);
3025
3026     },
3027     hide : function()
3028     {
3029         if(this.fireEvent("beforehide", this) !== false){
3030             
3031             this.maskEl.removeClass('show');
3032             
3033             this.maskEl.dom.style.display = '';
3034             Roo.get(document.body).removeClass("x-body-masked");
3035             this.el.removeClass('in');
3036             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3037
3038             if(this.animate){ // why
3039                 this.el.addClass('hideing');
3040                 this.el.removeClass('show');
3041                 (function(){
3042                     if (!this.el.hasClass('hideing')) {
3043                         return; // it's been shown again...
3044                     }
3045                     
3046                     this.el.dom.style.display='';
3047
3048                     Roo.get(document.body).removeClass('modal-open');
3049                     this.el.removeClass('hideing');
3050                 }).defer(150,this);
3051                 
3052             }else{
3053                 this.el.removeClass('show');
3054                 this.el.dom.style.display='';
3055                 Roo.get(document.body).removeClass('modal-open');
3056
3057             }
3058             this.fireEvent('hide', this);
3059         }
3060     },
3061     isVisible : function()
3062     {
3063         
3064         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3065         
3066     },
3067
3068     addButton : function(str, cb)
3069     {
3070
3071
3072         var b = Roo.apply({}, { html : str } );
3073         b.xns = b.xns || Roo.bootstrap;
3074         b.xtype = b.xtype || 'Button';
3075         if (typeof(b.listeners) == 'undefined') {
3076             b.listeners = { click : cb.createDelegate(this)  };
3077         }
3078
3079         var btn = Roo.factory(b);
3080
3081         btn.render(this.getButtonContainer());
3082
3083         return btn;
3084
3085     },
3086
3087     setDefaultButton : function(btn)
3088     {
3089         //this.el.select('.modal-footer').()
3090     },
3091
3092     resizeTo: function(w,h)
3093     {
3094         this.dialogEl.setWidth(w);
3095         
3096         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
3097
3098         this.bodyEl.setHeight(h - diff);
3099         
3100         this.fireEvent('resize', this);
3101     },
3102     
3103     setContentSize  : function(w, h)
3104     {
3105
3106     },
3107     onButtonClick: function(btn,e)
3108     {
3109         //Roo.log([a,b,c]);
3110         this.fireEvent('btnclick', btn.name, e);
3111     },
3112      /**
3113      * Set the title of the Dialog
3114      * @param {String} str new Title
3115      */
3116     setTitle: function(str) {
3117         this.titleEl.dom.innerHTML = str;
3118     },
3119     /**
3120      * Set the body of the Dialog
3121      * @param {String} str new Title
3122      */
3123     setBody: function(str) {
3124         this.bodyEl.dom.innerHTML = str;
3125     },
3126     /**
3127      * Set the body of the Dialog using the template
3128      * @param {Obj} data - apply this data to the template and replace the body contents.
3129      */
3130     applyBody: function(obj)
3131     {
3132         if (!this.tmpl) {
3133             Roo.log("Error - using apply Body without a template");
3134             //code
3135         }
3136         this.tmpl.overwrite(this.bodyEl, obj);
3137     },
3138     
3139     getChildHeight : function(child_nodes)
3140     {
3141         if(
3142             !child_nodes ||
3143             child_nodes.length == 0
3144         ) {
3145             return;
3146         }
3147         
3148         var child_height = 0;
3149         
3150         for(var i = 0; i < child_nodes.length; i++) {
3151             
3152             /*
3153             * for modal with tabs...
3154             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3155                 
3156                 var layout_childs = child_nodes[i].childNodes;
3157                 
3158                 for(var j = 0; j < layout_childs.length; j++) {
3159                     
3160                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3161                         
3162                         var layout_body_childs = layout_childs[j].childNodes;
3163                         
3164                         for(var k = 0; k < layout_body_childs.length; k++) {
3165                             
3166                             if(layout_body_childs[k].classList.contains('navbar')) {
3167                                 child_height += layout_body_childs[k].offsetHeight;
3168                                 continue;
3169                             }
3170                             
3171                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3172                                 
3173                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3174                                 
3175                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3176                                     
3177                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3178                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3179                                         continue;
3180                                     }
3181                                     
3182                                 }
3183                                 
3184                             }
3185                             
3186                         }
3187                     }
3188                 }
3189                 continue;
3190             }
3191             */
3192             
3193             child_height += child_nodes[i].offsetHeight;
3194             // Roo.log(child_nodes[i].offsetHeight);
3195         }
3196         
3197         return child_height;
3198     }
3199
3200 });
3201
3202
3203 Roo.apply(Roo.bootstrap.Modal,  {
3204     /**
3205          * Button config that displays a single OK button
3206          * @type Object
3207          */
3208         OK :  [{
3209             name : 'ok',
3210             weight : 'primary',
3211             html : 'OK'
3212         }],
3213         /**
3214          * Button config that displays Yes and No buttons
3215          * @type Object
3216          */
3217         YESNO : [
3218             {
3219                 name  : 'no',
3220                 html : 'No'
3221             },
3222             {
3223                 name  :'yes',
3224                 weight : 'primary',
3225                 html : 'Yes'
3226             }
3227         ],
3228
3229         /**
3230          * Button config that displays OK and Cancel buttons
3231          * @type Object
3232          */
3233         OKCANCEL : [
3234             {
3235                name : 'cancel',
3236                 html : 'Cancel'
3237             },
3238             {
3239                 name : 'ok',
3240                 weight : 'primary',
3241                 html : 'OK'
3242             }
3243         ],
3244         /**
3245          * Button config that displays Yes, No and Cancel buttons
3246          * @type Object
3247          */
3248         YESNOCANCEL : [
3249             {
3250                 name : 'yes',
3251                 weight : 'primary',
3252                 html : 'Yes'
3253             },
3254             {
3255                 name : 'no',
3256                 html : 'No'
3257             },
3258             {
3259                 name : 'cancel',
3260                 html : 'Cancel'
3261             }
3262         ],
3263         
3264         zIndex : 10001
3265 });
3266 /*
3267  * - LGPL
3268  *
3269  * messagebox - can be used as a replace
3270  * 
3271  */
3272 /**
3273  * @class Roo.MessageBox
3274  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3275  * Example usage:
3276  *<pre><code>
3277 // Basic alert:
3278 Roo.Msg.alert('Status', 'Changes saved successfully.');
3279
3280 // Prompt for user data:
3281 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3282     if (btn == 'ok'){
3283         // process text value...
3284     }
3285 });
3286
3287 // Show a dialog using config options:
3288 Roo.Msg.show({
3289    title:'Save Changes?',
3290    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3291    buttons: Roo.Msg.YESNOCANCEL,
3292    fn: processResult,
3293    animEl: 'elId'
3294 });
3295 </code></pre>
3296  * @singleton
3297  */
3298 Roo.bootstrap.MessageBox = function(){
3299     var dlg, opt, mask, waitTimer;
3300     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3301     var buttons, activeTextEl, bwidth;
3302
3303     
3304     // private
3305     var handleButton = function(button){
3306         dlg.hide();
3307         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3308     };
3309
3310     // private
3311     var handleHide = function(){
3312         if(opt && opt.cls){
3313             dlg.el.removeClass(opt.cls);
3314         }
3315         //if(waitTimer){
3316         //    Roo.TaskMgr.stop(waitTimer);
3317         //    waitTimer = null;
3318         //}
3319     };
3320
3321     // private
3322     var updateButtons = function(b){
3323         var width = 0;
3324         if(!b){
3325             buttons["ok"].hide();
3326             buttons["cancel"].hide();
3327             buttons["yes"].hide();
3328             buttons["no"].hide();
3329             dlg.footerEl.hide();
3330             
3331             return width;
3332         }
3333         dlg.footerEl.show();
3334         for(var k in buttons){
3335             if(typeof buttons[k] != "function"){
3336                 if(b[k]){
3337                     buttons[k].show();
3338                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3339                     width += buttons[k].el.getWidth()+15;
3340                 }else{
3341                     buttons[k].hide();
3342                 }
3343             }
3344         }
3345         return width;
3346     };
3347
3348     // private
3349     var handleEsc = function(d, k, e){
3350         if(opt && opt.closable !== false){
3351             dlg.hide();
3352         }
3353         if(e){
3354             e.stopEvent();
3355         }
3356     };
3357
3358     return {
3359         /**
3360          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3361          * @return {Roo.BasicDialog} The BasicDialog element
3362          */
3363         getDialog : function(){
3364            if(!dlg){
3365                 dlg = new Roo.bootstrap.Modal( {
3366                     //draggable: true,
3367                     //resizable:false,
3368                     //constraintoviewport:false,
3369                     //fixedcenter:true,
3370                     //collapsible : false,
3371                     //shim:true,
3372                     //modal: true,
3373                 //    width: 'auto',
3374                   //  height:100,
3375                     //buttonAlign:"center",
3376                     closeClick : function(){
3377                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3378                             handleButton("no");
3379                         }else{
3380                             handleButton("cancel");
3381                         }
3382                     }
3383                 });
3384                 dlg.render();
3385                 dlg.on("hide", handleHide);
3386                 mask = dlg.mask;
3387                 //dlg.addKeyListener(27, handleEsc);
3388                 buttons = {};
3389                 this.buttons = buttons;
3390                 var bt = this.buttonText;
3391                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3392                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3393                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3394                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3395                 //Roo.log(buttons);
3396                 bodyEl = dlg.bodyEl.createChild({
3397
3398                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3399                         '<textarea class="roo-mb-textarea"></textarea>' +
3400                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3401                 });
3402                 msgEl = bodyEl.dom.firstChild;
3403                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3404                 textboxEl.enableDisplayMode();
3405                 textboxEl.addKeyListener([10,13], function(){
3406                     if(dlg.isVisible() && opt && opt.buttons){
3407                         if(opt.buttons.ok){
3408                             handleButton("ok");
3409                         }else if(opt.buttons.yes){
3410                             handleButton("yes");
3411                         }
3412                     }
3413                 });
3414                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3415                 textareaEl.enableDisplayMode();
3416                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3417                 progressEl.enableDisplayMode();
3418                 
3419                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3420                 var pf = progressEl.dom.firstChild;
3421                 if (pf) {
3422                     pp = Roo.get(pf.firstChild);
3423                     pp.setHeight(pf.offsetHeight);
3424                 }
3425                 
3426             }
3427             return dlg;
3428         },
3429
3430         /**
3431          * Updates the message box body text
3432          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3433          * the XHTML-compliant non-breaking space character '&amp;#160;')
3434          * @return {Roo.MessageBox} This message box
3435          */
3436         updateText : function(text)
3437         {
3438             if(!dlg.isVisible() && !opt.width){
3439                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3440                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3441             }
3442             msgEl.innerHTML = text || '&#160;';
3443       
3444             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3445             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3446             var w = Math.max(
3447                     Math.min(opt.width || cw , this.maxWidth), 
3448                     Math.max(opt.minWidth || this.minWidth, bwidth)
3449             );
3450             if(opt.prompt){
3451                 activeTextEl.setWidth(w);
3452             }
3453             if(dlg.isVisible()){
3454                 dlg.fixedcenter = false;
3455             }
3456             // to big, make it scroll. = But as usual stupid IE does not support
3457             // !important..
3458             
3459             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3460                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3461                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3462             } else {
3463                 bodyEl.dom.style.height = '';
3464                 bodyEl.dom.style.overflowY = '';
3465             }
3466             if (cw > w) {
3467                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3468             } else {
3469                 bodyEl.dom.style.overflowX = '';
3470             }
3471             
3472             dlg.setContentSize(w, bodyEl.getHeight());
3473             if(dlg.isVisible()){
3474                 dlg.fixedcenter = true;
3475             }
3476             return this;
3477         },
3478
3479         /**
3480          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3481          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3482          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3483          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3484          * @return {Roo.MessageBox} This message box
3485          */
3486         updateProgress : function(value, text){
3487             if(text){
3488                 this.updateText(text);
3489             }
3490             
3491             if (pp) { // weird bug on my firefox - for some reason this is not defined
3492                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3493                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3494             }
3495             return this;
3496         },        
3497
3498         /**
3499          * Returns true if the message box is currently displayed
3500          * @return {Boolean} True if the message box is visible, else false
3501          */
3502         isVisible : function(){
3503             return dlg && dlg.isVisible();  
3504         },
3505
3506         /**
3507          * Hides the message box if it is displayed
3508          */
3509         hide : function(){
3510             if(this.isVisible()){
3511                 dlg.hide();
3512             }  
3513         },
3514
3515         /**
3516          * Displays a new message box, or reinitializes an existing message box, based on the config options
3517          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3518          * The following config object properties are supported:
3519          * <pre>
3520 Property    Type             Description
3521 ----------  ---------------  ------------------------------------------------------------------------------------
3522 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3523                                    closes (defaults to undefined)
3524 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3525                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3526 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3527                                    progress and wait dialogs will ignore this property and always hide the
3528                                    close button as they can only be closed programmatically.
3529 cls               String           A custom CSS class to apply to the message box element
3530 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3531                                    displayed (defaults to 75)
3532 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3533                                    function will be btn (the name of the button that was clicked, if applicable,
3534                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3535                                    Progress and wait dialogs will ignore this option since they do not respond to
3536                                    user actions and can only be closed programmatically, so any required function
3537                                    should be called by the same code after it closes the dialog.
3538 icon              String           A CSS class that provides a background image to be used as an icon for
3539                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3540 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3541 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3542 modal             Boolean          False to allow user interaction with the page while the message box is
3543                                    displayed (defaults to true)
3544 msg               String           A string that will replace the existing message box body text (defaults
3545                                    to the XHTML-compliant non-breaking space character '&#160;')
3546 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3547 progress          Boolean          True to display a progress bar (defaults to false)
3548 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3549 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3550 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3551 title             String           The title text
3552 value             String           The string value to set into the active textbox element if displayed
3553 wait              Boolean          True to display a progress bar (defaults to false)
3554 width             Number           The width of the dialog in pixels
3555 </pre>
3556          *
3557          * Example usage:
3558          * <pre><code>
3559 Roo.Msg.show({
3560    title: 'Address',
3561    msg: 'Please enter your address:',
3562    width: 300,
3563    buttons: Roo.MessageBox.OKCANCEL,
3564    multiline: true,
3565    fn: saveAddress,
3566    animEl: 'addAddressBtn'
3567 });
3568 </code></pre>
3569          * @param {Object} config Configuration options
3570          * @return {Roo.MessageBox} This message box
3571          */
3572         show : function(options)
3573         {
3574             
3575             // this causes nightmares if you show one dialog after another
3576             // especially on callbacks..
3577              
3578             if(this.isVisible()){
3579                 
3580                 this.hide();
3581                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3582                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3583                 Roo.log("New Dialog Message:" +  options.msg )
3584                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3585                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3586                 
3587             }
3588             var d = this.getDialog();
3589             opt = options;
3590             d.setTitle(opt.title || "&#160;");
3591             d.closeEl.setDisplayed(opt.closable !== false);
3592             activeTextEl = textboxEl;
3593             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3594             if(opt.prompt){
3595                 if(opt.multiline){
3596                     textboxEl.hide();
3597                     textareaEl.show();
3598                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3599                         opt.multiline : this.defaultTextHeight);
3600                     activeTextEl = textareaEl;
3601                 }else{
3602                     textboxEl.show();
3603                     textareaEl.hide();
3604                 }
3605             }else{
3606                 textboxEl.hide();
3607                 textareaEl.hide();
3608             }
3609             progressEl.setDisplayed(opt.progress === true);
3610             if (opt.progress) {
3611                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3612             }
3613             this.updateProgress(0);
3614             activeTextEl.dom.value = opt.value || "";
3615             if(opt.prompt){
3616                 dlg.setDefaultButton(activeTextEl);
3617             }else{
3618                 var bs = opt.buttons;
3619                 var db = null;
3620                 if(bs && bs.ok){
3621                     db = buttons["ok"];
3622                 }else if(bs && bs.yes){
3623                     db = buttons["yes"];
3624                 }
3625                 dlg.setDefaultButton(db);
3626             }
3627             bwidth = updateButtons(opt.buttons);
3628             this.updateText(opt.msg);
3629             if(opt.cls){
3630                 d.el.addClass(opt.cls);
3631             }
3632             d.proxyDrag = opt.proxyDrag === true;
3633             d.modal = opt.modal !== false;
3634             d.mask = opt.modal !== false ? mask : false;
3635             if(!d.isVisible()){
3636                 // force it to the end of the z-index stack so it gets a cursor in FF
3637                 document.body.appendChild(dlg.el.dom);
3638                 d.animateTarget = null;
3639                 d.show(options.animEl);
3640             }
3641             return this;
3642         },
3643
3644         /**
3645          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3646          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3647          * and closing the message box when the process is complete.
3648          * @param {String} title The title bar text
3649          * @param {String} msg The message box body text
3650          * @return {Roo.MessageBox} This message box
3651          */
3652         progress : function(title, msg){
3653             this.show({
3654                 title : title,
3655                 msg : msg,
3656                 buttons: false,
3657                 progress:true,
3658                 closable:false,
3659                 minWidth: this.minProgressWidth,
3660                 modal : true
3661             });
3662             return this;
3663         },
3664
3665         /**
3666          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3667          * If a callback function is passed it will be called after the user clicks the button, and the
3668          * id of the button that was clicked will be passed as the only parameter to the callback
3669          * (could also be the top-right close button).
3670          * @param {String} title The title bar text
3671          * @param {String} msg The message box body text
3672          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3673          * @param {Object} scope (optional) The scope of the callback function
3674          * @return {Roo.MessageBox} This message box
3675          */
3676         alert : function(title, msg, fn, scope)
3677         {
3678             this.show({
3679                 title : title,
3680                 msg : msg,
3681                 buttons: this.OK,
3682                 fn: fn,
3683                 closable : false,
3684                 scope : scope,
3685                 modal : true
3686             });
3687             return this;
3688         },
3689
3690         /**
3691          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3692          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3693          * You are responsible for closing the message box when the process is complete.
3694          * @param {String} msg The message box body text
3695          * @param {String} title (optional) The title bar text
3696          * @return {Roo.MessageBox} This message box
3697          */
3698         wait : function(msg, title){
3699             this.show({
3700                 title : title,
3701                 msg : msg,
3702                 buttons: false,
3703                 closable:false,
3704                 progress:true,
3705                 modal:true,
3706                 width:300,
3707                 wait:true
3708             });
3709             waitTimer = Roo.TaskMgr.start({
3710                 run: function(i){
3711                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3712                 },
3713                 interval: 1000
3714             });
3715             return this;
3716         },
3717
3718         /**
3719          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3720          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3721          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3722          * @param {String} title The title bar text
3723          * @param {String} msg The message box body text
3724          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3725          * @param {Object} scope (optional) The scope of the callback function
3726          * @return {Roo.MessageBox} This message box
3727          */
3728         confirm : function(title, msg, fn, scope){
3729             this.show({
3730                 title : title,
3731                 msg : msg,
3732                 buttons: this.YESNO,
3733                 fn: fn,
3734                 scope : scope,
3735                 modal : true
3736             });
3737             return this;
3738         },
3739
3740         /**
3741          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3742          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3743          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3744          * (could also be the top-right close button) and the text that was entered will be passed as the two
3745          * parameters to the callback.
3746          * @param {String} title The title bar text
3747          * @param {String} msg The message box body text
3748          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3749          * @param {Object} scope (optional) The scope of the callback function
3750          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3751          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3752          * @return {Roo.MessageBox} This message box
3753          */
3754         prompt : function(title, msg, fn, scope, multiline){
3755             this.show({
3756                 title : title,
3757                 msg : msg,
3758                 buttons: this.OKCANCEL,
3759                 fn: fn,
3760                 minWidth:250,
3761                 scope : scope,
3762                 prompt:true,
3763                 multiline: multiline,
3764                 modal : true
3765             });
3766             return this;
3767         },
3768
3769         /**
3770          * Button config that displays a single OK button
3771          * @type Object
3772          */
3773         OK : {ok:true},
3774         /**
3775          * Button config that displays Yes and No buttons
3776          * @type Object
3777          */
3778         YESNO : {yes:true, no:true},
3779         /**
3780          * Button config that displays OK and Cancel buttons
3781          * @type Object
3782          */
3783         OKCANCEL : {ok:true, cancel:true},
3784         /**
3785          * Button config that displays Yes, No and Cancel buttons
3786          * @type Object
3787          */
3788         YESNOCANCEL : {yes:true, no:true, cancel:true},
3789
3790         /**
3791          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3792          * @type Number
3793          */
3794         defaultTextHeight : 75,
3795         /**
3796          * The maximum width in pixels of the message box (defaults to 600)
3797          * @type Number
3798          */
3799         maxWidth : 600,
3800         /**
3801          * The minimum width in pixels of the message box (defaults to 100)
3802          * @type Number
3803          */
3804         minWidth : 100,
3805         /**
3806          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3807          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3808          * @type Number
3809          */
3810         minProgressWidth : 250,
3811         /**
3812          * An object containing the default button text strings that can be overriden for localized language support.
3813          * Supported properties are: ok, cancel, yes and no.
3814          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3815          * @type Object
3816          */
3817         buttonText : {
3818             ok : "OK",
3819             cancel : "Cancel",
3820             yes : "Yes",
3821             no : "No"
3822         }
3823     };
3824 }();
3825
3826 /**
3827  * Shorthand for {@link Roo.MessageBox}
3828  */
3829 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3830 Roo.Msg = Roo.Msg || Roo.MessageBox;
3831 /*
3832  * - LGPL
3833  *
3834  * navbar
3835  * 
3836  */
3837
3838 /**
3839  * @class Roo.bootstrap.Navbar
3840  * @extends Roo.bootstrap.Component
3841  * Bootstrap Navbar class
3842
3843  * @constructor
3844  * Create a new Navbar
3845  * @param {Object} config The config object
3846  */
3847
3848
3849 Roo.bootstrap.Navbar = function(config){
3850     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3851     this.addEvents({
3852         // raw events
3853         /**
3854          * @event beforetoggle
3855          * Fire before toggle the menu
3856          * @param {Roo.EventObject} e
3857          */
3858         "beforetoggle" : true
3859     });
3860 };
3861
3862 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3863     
3864     
3865    
3866     // private
3867     navItems : false,
3868     loadMask : false,
3869     
3870     
3871     getAutoCreate : function(){
3872         
3873         
3874         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3875         
3876     },
3877     
3878     initEvents :function ()
3879     {
3880         //Roo.log(this.el.select('.navbar-toggle',true));
3881         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
3882         
3883         var mark = {
3884             tag: "div",
3885             cls:"x-dlg-mask"
3886         };
3887         
3888         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3889         
3890         var size = this.el.getSize();
3891         this.maskEl.setSize(size.width, size.height);
3892         this.maskEl.enableDisplayMode("block");
3893         this.maskEl.hide();
3894         
3895         if(this.loadMask){
3896             this.maskEl.show();
3897         }
3898     },
3899     
3900     
3901     getChildContainer : function()
3902     {
3903         if (this.el && this.el.select('.collapse').getCount()) {
3904             return this.el.select('.collapse',true).first();
3905         }
3906         
3907         return this.el;
3908     },
3909     
3910     mask : function()
3911     {
3912         this.maskEl.show();
3913     },
3914     
3915     unmask : function()
3916     {
3917         this.maskEl.hide();
3918     },
3919     onToggle : function()
3920     {
3921         
3922         if(this.fireEvent('beforetoggle', this) === false){
3923             return;
3924         }
3925         var ce = this.el.select('.navbar-collapse',true).first();
3926       
3927         if (!ce.hasClass('show')) {
3928            this.expand();
3929         } else {
3930             this.collapse();
3931         }
3932         
3933         
3934     
3935     },
3936     /**
3937      * Expand the navbar pulldown 
3938      */
3939     expand : function ()
3940     {
3941        
3942         var ce = this.el.select('.navbar-collapse',true).first();
3943         if (ce.hasClass('collapsing')) {
3944             return;
3945         }
3946         ce.dom.style.height = '';
3947                // show it...
3948         ce.addClass('in'); // old...
3949         ce.removeClass('collapse');
3950         ce.addClass('show');
3951         var h = ce.getHeight();
3952         Roo.log(h);
3953         ce.removeClass('show');
3954         // at this point we should be able to see it..
3955         ce.addClass('collapsing');
3956         
3957         ce.setHeight(0); // resize it ...
3958         ce.on('transitionend', function() {
3959             //Roo.log('done transition');
3960             ce.removeClass('collapsing');
3961             ce.addClass('show');
3962             ce.removeClass('collapse');
3963
3964             ce.dom.style.height = '';
3965         }, this, { single: true} );
3966         ce.setHeight(h);
3967         ce.dom.scrollTop = 0;
3968     },
3969     /**
3970      * Collapse the navbar pulldown 
3971      */
3972     collapse : function()
3973     {
3974          var ce = this.el.select('.navbar-collapse',true).first();
3975        
3976         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
3977             // it's collapsed or collapsing..
3978             return;
3979         }
3980         ce.removeClass('in'); // old...
3981         ce.setHeight(ce.getHeight());
3982         ce.removeClass('show');
3983         ce.addClass('collapsing');
3984         
3985         ce.on('transitionend', function() {
3986             ce.dom.style.height = '';
3987             ce.removeClass('collapsing');
3988             ce.addClass('collapse');
3989         }, this, { single: true} );
3990         ce.setHeight(0);
3991     }
3992     
3993     
3994     
3995 });
3996
3997
3998
3999  
4000
4001  /*
4002  * - LGPL
4003  *
4004  * navbar
4005  * 
4006  */
4007
4008 /**
4009  * @class Roo.bootstrap.NavSimplebar
4010  * @extends Roo.bootstrap.Navbar
4011  * Bootstrap Sidebar class
4012  *
4013  * @cfg {Boolean} inverse is inverted color
4014  * 
4015  * @cfg {String} type (nav | pills | tabs)
4016  * @cfg {Boolean} arrangement stacked | justified
4017  * @cfg {String} align (left | right) alignment
4018  * 
4019  * @cfg {Boolean} main (true|false) main nav bar? default false
4020  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4021  * 
4022  * @cfg {String} tag (header|footer|nav|div) default is nav 
4023
4024  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4025  * 
4026  * 
4027  * @constructor
4028  * Create a new Sidebar
4029  * @param {Object} config The config object
4030  */
4031
4032
4033 Roo.bootstrap.NavSimplebar = function(config){
4034     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4035 };
4036
4037 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4038     
4039     inverse: false,
4040     
4041     type: false,
4042     arrangement: '',
4043     align : false,
4044     
4045     weight : 'light',
4046     
4047     main : false,
4048     
4049     
4050     tag : false,
4051     
4052     
4053     getAutoCreate : function(){
4054         
4055         
4056         var cfg = {
4057             tag : this.tag || 'div',
4058             cls : 'navbar navbar-expand-lg roo-navbar-simple'
4059         };
4060         if (['light','white'].indexOf(this.weight) > -1) {
4061             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4062         }
4063         cfg.cls += ' bg-' + this.weight;
4064         
4065         if (this.inverse) {
4066             cfg.cls += ' navbar-inverse';
4067             
4068         }
4069         
4070         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4071         
4072         //if (Roo.bootstrap.version == 4) {
4073         //    return cfg;
4074         //}
4075         
4076         cfg.cn = [
4077             {
4078                 cls: 'nav',
4079                 tag : 'ul'
4080             }
4081         ];
4082         
4083          
4084         this.type = this.type || 'nav';
4085         if (['tabs','pills'].indexOf(this.type) != -1) {
4086             cfg.cn[0].cls += ' nav-' + this.type
4087         
4088         
4089         } else {
4090             if (this.type!=='nav') {
4091                 Roo.log('nav type must be nav/tabs/pills')
4092             }
4093             cfg.cn[0].cls += ' navbar-nav'
4094         }
4095         
4096         
4097         
4098         
4099         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4100             cfg.cn[0].cls += ' nav-' + this.arrangement;
4101         }
4102         
4103         
4104         if (this.align === 'right') {
4105             cfg.cn[0].cls += ' navbar-right';
4106         }
4107         
4108         
4109         
4110         
4111         return cfg;
4112     
4113         
4114     }
4115     
4116     
4117     
4118 });
4119
4120
4121
4122  
4123
4124  
4125        /*
4126  * - LGPL
4127  *
4128  * navbar
4129  * navbar-fixed-top
4130  * navbar-expand-md  fixed-top 
4131  */
4132
4133 /**
4134  * @class Roo.bootstrap.NavHeaderbar
4135  * @extends Roo.bootstrap.NavSimplebar
4136  * Bootstrap Sidebar class
4137  *
4138  * @cfg {String} brand what is brand
4139  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4140  * @cfg {String} brand_href href of the brand
4141  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4142  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4143  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4144  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4145  * 
4146  * @constructor
4147  * Create a new Sidebar
4148  * @param {Object} config The config object
4149  */
4150
4151
4152 Roo.bootstrap.NavHeaderbar = function(config){
4153     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4154       
4155 };
4156
4157 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4158     
4159     position: '',
4160     brand: '',
4161     brand_href: false,
4162     srButton : true,
4163     autohide : false,
4164     desktopCenter : false,
4165    
4166     
4167     getAutoCreate : function(){
4168         
4169         var   cfg = {
4170             tag: this.nav || 'nav',
4171             cls: 'navbar navbar-expand-md',
4172             role: 'navigation',
4173             cn: []
4174         };
4175         
4176         var cn = cfg.cn;
4177         if (this.desktopCenter) {
4178             cn.push({cls : 'container', cn : []});
4179             cn = cn[0].cn;
4180         }
4181         
4182         if(this.srButton){
4183             var btn = {
4184                 tag: 'button',
4185                 type: 'button',
4186                 cls: 'navbar-toggle navbar-toggler',
4187                 'data-toggle': 'collapse',
4188                 cn: [
4189                     {
4190                         tag: 'span',
4191                         cls: 'sr-only',
4192                         html: 'Toggle navigation'
4193                     },
4194                     {
4195                         tag: 'span',
4196                         cls: 'icon-bar navbar-toggler-icon'
4197                     },
4198                     {
4199                         tag: 'span',
4200                         cls: 'icon-bar'
4201                     },
4202                     {
4203                         tag: 'span',
4204                         cls: 'icon-bar'
4205                     }
4206                 ]
4207             };
4208             
4209             cn.push( Roo.bootstrap.version == 4 ? btn : {
4210                 tag: 'div',
4211                 cls: 'navbar-header',
4212                 cn: [
4213                     btn
4214                 ]
4215             });
4216         }
4217         
4218         cn.push({
4219             tag: 'div',
4220             cls: 'collapse navbar-collapse',
4221             cn : []
4222         });
4223         
4224         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4225         
4226         if (['light','white'].indexOf(this.weight) > -1) {
4227             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4228         }
4229         cfg.cls += ' bg-' + this.weight;
4230         
4231         
4232         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4233             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4234             
4235             // tag can override this..
4236             
4237             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4238         }
4239         
4240         if (this.brand !== '') {
4241             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4242             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4243                 tag: 'a',
4244                 href: this.brand_href ? this.brand_href : '#',
4245                 cls: 'navbar-brand',
4246                 cn: [
4247                 this.brand
4248                 ]
4249             });
4250         }
4251         
4252         if(this.main){
4253             cfg.cls += ' main-nav';
4254         }
4255         
4256         
4257         return cfg;
4258
4259         
4260     },
4261     getHeaderChildContainer : function()
4262     {
4263         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4264             return this.el.select('.navbar-header',true).first();
4265         }
4266         
4267         return this.getChildContainer();
4268     },
4269     
4270     
4271     initEvents : function()
4272     {
4273         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4274         
4275         if (this.autohide) {
4276             
4277             var prevScroll = 0;
4278             var ft = this.el;
4279             
4280             Roo.get(document).on('scroll',function(e) {
4281                 var ns = Roo.get(document).getScroll().top;
4282                 var os = prevScroll;
4283                 prevScroll = ns;
4284                 
4285                 if(ns > os){
4286                     ft.removeClass('slideDown');
4287                     ft.addClass('slideUp');
4288                     return;
4289                 }
4290                 ft.removeClass('slideUp');
4291                 ft.addClass('slideDown');
4292                  
4293               
4294           },this);
4295         }
4296     }    
4297     
4298 });
4299
4300
4301
4302  
4303
4304  /*
4305  * - LGPL
4306  *
4307  * navbar
4308  * 
4309  */
4310
4311 /**
4312  * @class Roo.bootstrap.NavSidebar
4313  * @extends Roo.bootstrap.Navbar
4314  * Bootstrap Sidebar class
4315  * 
4316  * @constructor
4317  * Create a new Sidebar
4318  * @param {Object} config The config object
4319  */
4320
4321
4322 Roo.bootstrap.NavSidebar = function(config){
4323     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4324 };
4325
4326 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4327     
4328     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4329     
4330     getAutoCreate : function(){
4331         
4332         
4333         return  {
4334             tag: 'div',
4335             cls: 'sidebar sidebar-nav'
4336         };
4337     
4338         
4339     }
4340     
4341     
4342     
4343 });
4344
4345
4346
4347  
4348
4349  /*
4350  * - LGPL
4351  *
4352  * nav group
4353  * 
4354  */
4355
4356 /**
4357  * @class Roo.bootstrap.NavGroup
4358  * @extends Roo.bootstrap.Component
4359  * Bootstrap NavGroup class
4360  * @cfg {String} align (left|right)
4361  * @cfg {Boolean} inverse
4362  * @cfg {String} type (nav|pills|tab) default nav
4363  * @cfg {String} navId - reference Id for navbar.
4364
4365  * 
4366  * @constructor
4367  * Create a new nav group
4368  * @param {Object} config The config object
4369  */
4370
4371 Roo.bootstrap.NavGroup = function(config){
4372     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4373     this.navItems = [];
4374    
4375     Roo.bootstrap.NavGroup.register(this);
4376      this.addEvents({
4377         /**
4378              * @event changed
4379              * Fires when the active item changes
4380              * @param {Roo.bootstrap.NavGroup} this
4381              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4382              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4383          */
4384         'changed': true
4385      });
4386     
4387 };
4388
4389 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4390     
4391     align: '',
4392     inverse: false,
4393     form: false,
4394     type: 'nav',
4395     navId : '',
4396     // private
4397     
4398     navItems : false, 
4399     
4400     getAutoCreate : function()
4401     {
4402         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4403         
4404         cfg = {
4405             tag : 'ul',
4406             cls: 'nav' 
4407         };
4408         if (Roo.bootstrap.version == 4) {
4409             if (['tabs','pills'].indexOf(this.type) != -1) {
4410                 cfg.cls += ' nav-' + this.type; 
4411             } else {
4412                 cfg.cls += ' navbar-nav';
4413             }
4414         } else {
4415             if (['tabs','pills'].indexOf(this.type) != -1) {
4416                 cfg.cls += ' nav-' + this.type
4417             } else {
4418                 if (this.type !== 'nav') {
4419                     Roo.log('nav type must be nav/tabs/pills')
4420                 }
4421                 cfg.cls += ' navbar-nav'
4422             }
4423         }
4424         
4425         if (this.parent() && this.parent().sidebar) {
4426             cfg = {
4427                 tag: 'ul',
4428                 cls: 'dashboard-menu sidebar-menu'
4429             };
4430             
4431             return cfg;
4432         }
4433         
4434         if (this.form === true) {
4435             cfg = {
4436                 tag: 'form',
4437                 cls: 'navbar-form form-inline'
4438             };
4439             
4440             if (this.align === 'right') {
4441                 cfg.cls += ' navbar-right ml-md-auto';
4442             } else {
4443                 cfg.cls += ' navbar-left';
4444             }
4445         }
4446         
4447         if (this.align === 'right') {
4448             cfg.cls += ' navbar-right ml-md-auto';
4449         } else {
4450             cfg.cls += ' mr-auto';
4451         }
4452         
4453         if (this.inverse) {
4454             cfg.cls += ' navbar-inverse';
4455             
4456         }
4457         
4458         
4459         return cfg;
4460     },
4461     /**
4462     * sets the active Navigation item
4463     * @param {Roo.bootstrap.NavItem} the new current navitem
4464     */
4465     setActiveItem : function(item)
4466     {
4467         var prev = false;
4468         Roo.each(this.navItems, function(v){
4469             if (v == item) {
4470                 return ;
4471             }
4472             if (v.isActive()) {
4473                 v.setActive(false, true);
4474                 prev = v;
4475                 
4476             }
4477             
4478         });
4479
4480         item.setActive(true, true);
4481         this.fireEvent('changed', this, item, prev);
4482         
4483         
4484     },
4485     /**
4486     * gets the active Navigation item
4487     * @return {Roo.bootstrap.NavItem} the current navitem
4488     */
4489     getActive : function()
4490     {
4491         
4492         var prev = false;
4493         Roo.each(this.navItems, function(v){
4494             
4495             if (v.isActive()) {
4496                 prev = v;
4497                 
4498             }
4499             
4500         });
4501         return prev;
4502     },
4503     
4504     indexOfNav : function()
4505     {
4506         
4507         var prev = false;
4508         Roo.each(this.navItems, function(v,i){
4509             
4510             if (v.isActive()) {
4511                 prev = i;
4512                 
4513             }
4514             
4515         });
4516         return prev;
4517     },
4518     /**
4519     * adds a Navigation item
4520     * @param {Roo.bootstrap.NavItem} the navitem to add
4521     */
4522     addItem : function(cfg)
4523     {
4524         if (this.form && Roo.bootstrap.version == 4) {
4525             cfg.tag = 'div';
4526         }
4527         var cn = new Roo.bootstrap.NavItem(cfg);
4528         this.register(cn);
4529         cn.parentId = this.id;
4530         cn.onRender(this.el, null);
4531         return cn;
4532     },
4533     /**
4534     * register a Navigation item
4535     * @param {Roo.bootstrap.NavItem} the navitem to add
4536     */
4537     register : function(item)
4538     {
4539         this.navItems.push( item);
4540         item.navId = this.navId;
4541     
4542     },
4543     
4544     /**
4545     * clear all the Navigation item
4546     */
4547    
4548     clearAll : function()
4549     {
4550         this.navItems = [];
4551         this.el.dom.innerHTML = '';
4552     },
4553     
4554     getNavItem: function(tabId)
4555     {
4556         var ret = false;
4557         Roo.each(this.navItems, function(e) {
4558             if (e.tabId == tabId) {
4559                ret =  e;
4560                return false;
4561             }
4562             return true;
4563             
4564         });
4565         return ret;
4566     },
4567     
4568     setActiveNext : function()
4569     {
4570         var i = this.indexOfNav(this.getActive());
4571         if (i > this.navItems.length) {
4572             return;
4573         }
4574         this.setActiveItem(this.navItems[i+1]);
4575     },
4576     setActivePrev : function()
4577     {
4578         var i = this.indexOfNav(this.getActive());
4579         if (i  < 1) {
4580             return;
4581         }
4582         this.setActiveItem(this.navItems[i-1]);
4583     },
4584     clearWasActive : function(except) {
4585         Roo.each(this.navItems, function(e) {
4586             if (e.tabId != except.tabId && e.was_active) {
4587                e.was_active = false;
4588                return false;
4589             }
4590             return true;
4591             
4592         });
4593     },
4594     getWasActive : function ()
4595     {
4596         var r = false;
4597         Roo.each(this.navItems, function(e) {
4598             if (e.was_active) {
4599                r = e;
4600                return false;
4601             }
4602             return true;
4603             
4604         });
4605         return r;
4606     }
4607     
4608     
4609 });
4610
4611  
4612 Roo.apply(Roo.bootstrap.NavGroup, {
4613     
4614     groups: {},
4615      /**
4616     * register a Navigation Group
4617     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4618     */
4619     register : function(navgrp)
4620     {
4621         this.groups[navgrp.navId] = navgrp;
4622         
4623     },
4624     /**
4625     * fetch a Navigation Group based on the navigation ID
4626     * @param {string} the navgroup to add
4627     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4628     */
4629     get: function(navId) {
4630         if (typeof(this.groups[navId]) == 'undefined') {
4631             return false;
4632             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4633         }
4634         return this.groups[navId] ;
4635     }
4636     
4637     
4638     
4639 });
4640
4641  /*
4642  * - LGPL
4643  *
4644  * row
4645  * 
4646  */
4647
4648 /**
4649  * @class Roo.bootstrap.NavItem
4650  * @extends Roo.bootstrap.Component
4651  * Bootstrap Navbar.NavItem class
4652  * @cfg {String} href  link to
4653  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4654
4655  * @cfg {String} html content of button
4656  * @cfg {String} badge text inside badge
4657  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4658  * @cfg {String} glyphicon DEPRICATED - use fa
4659  * @cfg {String} icon DEPRICATED - use fa
4660  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4661  * @cfg {Boolean} active Is item active
4662  * @cfg {Boolean} disabled Is item disabled
4663  
4664  * @cfg {Boolean} preventDefault (true | false) default false
4665  * @cfg {String} tabId the tab that this item activates.
4666  * @cfg {String} tagtype (a|span) render as a href or span?
4667  * @cfg {Boolean} animateRef (true|false) link to element default false  
4668   
4669  * @constructor
4670  * Create a new Navbar Item
4671  * @param {Object} config The config object
4672  */
4673 Roo.bootstrap.NavItem = function(config){
4674     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4675     this.addEvents({
4676         // raw events
4677         /**
4678          * @event click
4679          * The raw click event for the entire grid.
4680          * @param {Roo.EventObject} e
4681          */
4682         "click" : true,
4683          /**
4684             * @event changed
4685             * Fires when the active item active state changes
4686             * @param {Roo.bootstrap.NavItem} this
4687             * @param {boolean} state the new state
4688              
4689          */
4690         'changed': true,
4691         /**
4692             * @event scrollto
4693             * Fires when scroll to element
4694             * @param {Roo.bootstrap.NavItem} this
4695             * @param {Object} options
4696             * @param {Roo.EventObject} e
4697              
4698          */
4699         'scrollto': true
4700     });
4701    
4702 };
4703
4704 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4705     
4706     href: false,
4707     html: '',
4708     badge: '',
4709     icon: false,
4710     fa : false,
4711     glyphicon: false,
4712     active: false,
4713     preventDefault : false,
4714     tabId : false,
4715     tagtype : 'a',
4716     tag: 'li',
4717     disabled : false,
4718     animateRef : false,
4719     was_active : false,
4720     button_weight : '',
4721     button_outline : false,
4722     
4723     navLink: false,
4724     
4725     getAutoCreate : function(){
4726          
4727         var cfg = {
4728             tag: this.tag,
4729             cls: 'nav-item'
4730         };
4731         
4732         if (this.active) {
4733             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4734         }
4735         if (this.disabled) {
4736             cfg.cls += ' disabled';
4737         }
4738         
4739         // BS4 only?
4740         if (this.button_weight.length) {
4741             cfg.tag = this.href ? 'a' : 'button';
4742             cfg.html = this.html || '';
4743             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4744             if (this.href) {
4745                 cfg.href = this.href;
4746             }
4747             if (this.fa) {
4748                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
4749             }
4750             
4751             // menu .. should add dropdown-menu class - so no need for carat..
4752             
4753             if (this.badge !== '') {
4754                  
4755                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4756             }
4757             return cfg;
4758         }
4759         
4760         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4761             cfg.cn = [
4762                 {
4763                     tag: this.tagtype,
4764                     href : this.href || "#",
4765                     html: this.html || ''
4766                 }
4767             ];
4768             if (this.tagtype == 'a') {
4769                 cfg.cn[0].cls = 'nav-link';
4770             }
4771             if (this.icon) {
4772                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
4773             }
4774             if (this.fa) {
4775                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
4776             }
4777             if(this.glyphicon) {
4778                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4779             }
4780             
4781             if (this.menu) {
4782                 
4783                 cfg.cn[0].html += " <span class='caret'></span>";
4784              
4785             }
4786             
4787             if (this.badge !== '') {
4788                  
4789                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4790             }
4791         }
4792         
4793         
4794         
4795         return cfg;
4796     },
4797     onRender : function(ct, position)
4798     {
4799        // Roo.log("Call onRender: " + this.xtype);
4800         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4801             this.tag = 'div';
4802         }
4803         
4804         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4805         this.navLink = this.el.select('.nav-link',true).first();
4806         return ret;
4807     },
4808       
4809     
4810     initEvents: function() 
4811     {
4812         if (typeof (this.menu) != 'undefined') {
4813             this.menu.parentType = this.xtype;
4814             this.menu.triggerEl = this.el;
4815             this.menu = this.addxtype(Roo.apply({}, this.menu));
4816         }
4817         
4818         this.el.select('a',true).on('click', this.onClick, this);
4819         
4820         if(this.tagtype == 'span'){
4821             this.el.select('span',true).on('click', this.onClick, this);
4822         }
4823        
4824         // at this point parent should be available..
4825         this.parent().register(this);
4826     },
4827     
4828     onClick : function(e)
4829     {
4830         if (e.getTarget('.dropdown-menu-item')) {
4831             // did you click on a menu itemm.... - then don't trigger onclick..
4832             return;
4833         }
4834         
4835         if(
4836                 this.preventDefault || 
4837                 this.href == '#' 
4838         ){
4839             Roo.log("NavItem - prevent Default?");
4840             e.preventDefault();
4841         }
4842         
4843         if (this.disabled) {
4844             return;
4845         }
4846         
4847         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4848         if (tg && tg.transition) {
4849             Roo.log("waiting for the transitionend");
4850             return;
4851         }
4852         
4853         
4854         
4855         //Roo.log("fire event clicked");
4856         if(this.fireEvent('click', this, e) === false){
4857             return;
4858         };
4859         
4860         if(this.tagtype == 'span'){
4861             return;
4862         }
4863         
4864         //Roo.log(this.href);
4865         var ael = this.el.select('a',true).first();
4866         //Roo.log(ael);
4867         
4868         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4869             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4870             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4871                 return; // ignore... - it's a 'hash' to another page.
4872             }
4873             Roo.log("NavItem - prevent Default?");
4874             e.preventDefault();
4875             this.scrollToElement(e);
4876         }
4877         
4878         
4879         var p =  this.parent();
4880    
4881         if (['tabs','pills'].indexOf(p.type)!==-1) {
4882             if (typeof(p.setActiveItem) !== 'undefined') {
4883                 p.setActiveItem(this);
4884             }
4885         }
4886         
4887         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4888         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4889             // remove the collapsed menu expand...
4890             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4891         }
4892     },
4893     
4894     isActive: function () {
4895         return this.active
4896     },
4897     setActive : function(state, fire, is_was_active)
4898     {
4899         if (this.active && !state && this.navId) {
4900             this.was_active = true;
4901             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4902             if (nv) {
4903                 nv.clearWasActive(this);
4904             }
4905             
4906         }
4907         this.active = state;
4908         
4909         if (!state ) {
4910             this.el.removeClass('active');
4911             this.navLink ? this.navLink.removeClass('active') : false;
4912         } else if (!this.el.hasClass('active')) {
4913             
4914             this.el.addClass('active');
4915             if (Roo.bootstrap.version == 4 && this.navLink ) {
4916                 this.navLink.addClass('active');
4917             }
4918             
4919         }
4920         if (fire) {
4921             this.fireEvent('changed', this, state);
4922         }
4923         
4924         // show a panel if it's registered and related..
4925         
4926         if (!this.navId || !this.tabId || !state || is_was_active) {
4927             return;
4928         }
4929         
4930         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4931         if (!tg) {
4932             return;
4933         }
4934         var pan = tg.getPanelByName(this.tabId);
4935         if (!pan) {
4936             return;
4937         }
4938         // if we can not flip to new panel - go back to old nav highlight..
4939         if (false == tg.showPanel(pan)) {
4940             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4941             if (nv) {
4942                 var onav = nv.getWasActive();
4943                 if (onav) {
4944                     onav.setActive(true, false, true);
4945                 }
4946             }
4947             
4948         }
4949         
4950         
4951         
4952     },
4953      // this should not be here...
4954     setDisabled : function(state)
4955     {
4956         this.disabled = state;
4957         if (!state ) {
4958             this.el.removeClass('disabled');
4959         } else if (!this.el.hasClass('disabled')) {
4960             this.el.addClass('disabled');
4961         }
4962         
4963     },
4964     
4965     /**
4966      * Fetch the element to display the tooltip on.
4967      * @return {Roo.Element} defaults to this.el
4968      */
4969     tooltipEl : function()
4970     {
4971         return this.el.select('' + this.tagtype + '', true).first();
4972     },
4973     
4974     scrollToElement : function(e)
4975     {
4976         var c = document.body;
4977         
4978         /*
4979          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4980          */
4981         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4982             c = document.documentElement;
4983         }
4984         
4985         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4986         
4987         if(!target){
4988             return;
4989         }
4990
4991         var o = target.calcOffsetsTo(c);
4992         
4993         var options = {
4994             target : target,
4995             value : o[1]
4996         };
4997         
4998         this.fireEvent('scrollto', this, options, e);
4999         
5000         Roo.get(c).scrollTo('top', options.value, true);
5001         
5002         return;
5003     }
5004 });
5005  
5006
5007  /*
5008  * - LGPL
5009  *
5010  * sidebar item
5011  *
5012  *  li
5013  *    <span> icon </span>
5014  *    <span> text </span>
5015  *    <span>badge </span>
5016  */
5017
5018 /**
5019  * @class Roo.bootstrap.NavSidebarItem
5020  * @extends Roo.bootstrap.NavItem
5021  * Bootstrap Navbar.NavSidebarItem class
5022  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5023  * {Boolean} open is the menu open
5024  * {Boolean} buttonView use button as the tigger el rather that a (default false)
5025  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5026  * {String} buttonSize (sm|md|lg)the extra classes for the button
5027  * {Boolean} showArrow show arrow next to the text (default true)
5028  * @constructor
5029  * Create a new Navbar Button
5030  * @param {Object} config The config object
5031  */
5032 Roo.bootstrap.NavSidebarItem = function(config){
5033     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5034     this.addEvents({
5035         // raw events
5036         /**
5037          * @event click
5038          * The raw click event for the entire grid.
5039          * @param {Roo.EventObject} e
5040          */
5041         "click" : true,
5042          /**
5043             * @event changed
5044             * Fires when the active item active state changes
5045             * @param {Roo.bootstrap.NavSidebarItem} this
5046             * @param {boolean} state the new state
5047              
5048          */
5049         'changed': true
5050     });
5051    
5052 };
5053
5054 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
5055     
5056     badgeWeight : 'default',
5057     
5058     open: false,
5059     
5060     buttonView : false,
5061     
5062     buttonWeight : 'default',
5063     
5064     buttonSize : 'md',
5065     
5066     showArrow : true,
5067     
5068     getAutoCreate : function(){
5069         
5070         
5071         var a = {
5072                 tag: 'a',
5073                 href : this.href || '#',
5074                 cls: '',
5075                 html : '',
5076                 cn : []
5077         };
5078         
5079         if(this.buttonView){
5080             a = {
5081                 tag: 'button',
5082                 href : this.href || '#',
5083                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5084                 html : this.html,
5085                 cn : []
5086             };
5087         }
5088         
5089         var cfg = {
5090             tag: 'li',
5091             cls: '',
5092             cn: [ a ]
5093         };
5094         
5095         if (this.active) {
5096             cfg.cls += ' active';
5097         }
5098         
5099         if (this.disabled) {
5100             cfg.cls += ' disabled';
5101         }
5102         if (this.open) {
5103             cfg.cls += ' open x-open';
5104         }
5105         // left icon..
5106         if (this.glyphicon || this.icon) {
5107             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5108             a.cn.push({ tag : 'i', cls : c }) ;
5109         }
5110         
5111         if(!this.buttonView){
5112             var span = {
5113                 tag: 'span',
5114                 html : this.html || ''
5115             };
5116
5117             a.cn.push(span);
5118             
5119         }
5120         
5121         if (this.badge !== '') {
5122             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5123         }
5124         
5125         if (this.menu) {
5126             
5127             if(this.showArrow){
5128                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5129             }
5130             
5131             a.cls += ' dropdown-toggle treeview' ;
5132         }
5133         
5134         return cfg;
5135     },
5136     
5137     initEvents : function()
5138     { 
5139         if (typeof (this.menu) != 'undefined') {
5140             this.menu.parentType = this.xtype;
5141             this.menu.triggerEl = this.el;
5142             this.menu = this.addxtype(Roo.apply({}, this.menu));
5143         }
5144         
5145         this.el.on('click', this.onClick, this);
5146         
5147         if(this.badge !== ''){
5148             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5149         }
5150         
5151     },
5152     
5153     onClick : function(e)
5154     {
5155         if(this.disabled){
5156             e.preventDefault();
5157             return;
5158         }
5159         
5160         if(this.preventDefault){
5161             e.preventDefault();
5162         }
5163         
5164         this.fireEvent('click', this, e);
5165     },
5166     
5167     disable : function()
5168     {
5169         this.setDisabled(true);
5170     },
5171     
5172     enable : function()
5173     {
5174         this.setDisabled(false);
5175     },
5176     
5177     setDisabled : function(state)
5178     {
5179         if(this.disabled == state){
5180             return;
5181         }
5182         
5183         this.disabled = state;
5184         
5185         if (state) {
5186             this.el.addClass('disabled');
5187             return;
5188         }
5189         
5190         this.el.removeClass('disabled');
5191         
5192         return;
5193     },
5194     
5195     setActive : function(state)
5196     {
5197         if(this.active == state){
5198             return;
5199         }
5200         
5201         this.active = state;
5202         
5203         if (state) {
5204             this.el.addClass('active');
5205             return;
5206         }
5207         
5208         this.el.removeClass('active');
5209         
5210         return;
5211     },
5212     
5213     isActive: function () 
5214     {
5215         return this.active;
5216     },
5217     
5218     setBadge : function(str)
5219     {
5220         if(!this.badgeEl){
5221             return;
5222         }
5223         
5224         this.badgeEl.dom.innerHTML = str;
5225     }
5226     
5227    
5228      
5229  
5230 });
5231  
5232
5233  /*
5234  * - LGPL
5235  *
5236  * row
5237  * 
5238  */
5239
5240 /**
5241  * @class Roo.bootstrap.Row
5242  * @extends Roo.bootstrap.Component
5243  * Bootstrap Row class (contains columns...)
5244  * 
5245  * @constructor
5246  * Create a new Row
5247  * @param {Object} config The config object
5248  */
5249
5250 Roo.bootstrap.Row = function(config){
5251     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5252 };
5253
5254 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5255     
5256     getAutoCreate : function(){
5257        return {
5258             cls: 'row clearfix'
5259        };
5260     }
5261     
5262     
5263 });
5264
5265  
5266
5267  /*
5268  * - LGPL
5269  *
5270  * element
5271  * 
5272  */
5273
5274 /**
5275  * @class Roo.bootstrap.Element
5276  * @extends Roo.bootstrap.Component
5277  * Bootstrap Element class
5278  * @cfg {String} html contents of the element
5279  * @cfg {String} tag tag of the element
5280  * @cfg {String} cls class of the element
5281  * @cfg {Boolean} preventDefault (true|false) default false
5282  * @cfg {Boolean} clickable (true|false) default false
5283  * 
5284  * @constructor
5285  * Create a new Element
5286  * @param {Object} config The config object
5287  */
5288
5289 Roo.bootstrap.Element = function(config){
5290     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5291     
5292     this.addEvents({
5293         // raw events
5294         /**
5295          * @event click
5296          * When a element is chick
5297          * @param {Roo.bootstrap.Element} this
5298          * @param {Roo.EventObject} e
5299          */
5300         "click" : true
5301     });
5302 };
5303
5304 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5305     
5306     tag: 'div',
5307     cls: '',
5308     html: '',
5309     preventDefault: false, 
5310     clickable: false,
5311     
5312     getAutoCreate : function(){
5313         
5314         var cfg = {
5315             tag: this.tag,
5316             // cls: this.cls, double assign in parent class Component.js :: onRender
5317             html: this.html
5318         };
5319         
5320         return cfg;
5321     },
5322     
5323     initEvents: function() 
5324     {
5325         Roo.bootstrap.Element.superclass.initEvents.call(this);
5326         
5327         if(this.clickable){
5328             this.el.on('click', this.onClick, this);
5329         }
5330         
5331     },
5332     
5333     onClick : function(e)
5334     {
5335         if(this.preventDefault){
5336             e.preventDefault();
5337         }
5338         
5339         this.fireEvent('click', this, e);
5340     },
5341     
5342     getValue : function()
5343     {
5344         return this.el.dom.innerHTML;
5345     },
5346     
5347     setValue : function(value)
5348     {
5349         this.el.dom.innerHTML = value;
5350     }
5351    
5352 });
5353
5354  
5355
5356  /*
5357  * - LGPL
5358  *
5359  * pagination
5360  * 
5361  */
5362
5363 /**
5364  * @class Roo.bootstrap.Pagination
5365  * @extends Roo.bootstrap.Component
5366  * Bootstrap Pagination class
5367  * @cfg {String} size xs | sm | md | lg
5368  * @cfg {Boolean} inverse false | true
5369  * 
5370  * @constructor
5371  * Create a new Pagination
5372  * @param {Object} config The config object
5373  */
5374
5375 Roo.bootstrap.Pagination = function(config){
5376     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5377 };
5378
5379 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5380     
5381     cls: false,
5382     size: false,
5383     inverse: false,
5384     
5385     getAutoCreate : function(){
5386         var cfg = {
5387             tag: 'ul',
5388                 cls: 'pagination'
5389         };
5390         if (this.inverse) {
5391             cfg.cls += ' inverse';
5392         }
5393         if (this.html) {
5394             cfg.html=this.html;
5395         }
5396         if (this.cls) {
5397             cfg.cls += " " + this.cls;
5398         }
5399         return cfg;
5400     }
5401    
5402 });
5403
5404  
5405
5406  /*
5407  * - LGPL
5408  *
5409  * Pagination item
5410  * 
5411  */
5412
5413
5414 /**
5415  * @class Roo.bootstrap.PaginationItem
5416  * @extends Roo.bootstrap.Component
5417  * Bootstrap PaginationItem class
5418  * @cfg {String} html text
5419  * @cfg {String} href the link
5420  * @cfg {Boolean} preventDefault (true | false) default true
5421  * @cfg {Boolean} active (true | false) default false
5422  * @cfg {Boolean} disabled default false
5423  * 
5424  * 
5425  * @constructor
5426  * Create a new PaginationItem
5427  * @param {Object} config The config object
5428  */
5429
5430
5431 Roo.bootstrap.PaginationItem = function(config){
5432     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5433     this.addEvents({
5434         // raw events
5435         /**
5436          * @event click
5437          * The raw click event for the entire grid.
5438          * @param {Roo.EventObject} e
5439          */
5440         "click" : true
5441     });
5442 };
5443
5444 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5445     
5446     href : false,
5447     html : false,
5448     preventDefault: true,
5449     active : false,
5450     cls : false,
5451     disabled: false,
5452     
5453     getAutoCreate : function(){
5454         var cfg= {
5455             tag: 'li',
5456             cn: [
5457                 {
5458                     tag : 'a',
5459                     href : this.href ? this.href : '#',
5460                     html : this.html ? this.html : ''
5461                 }
5462             ]
5463         };
5464         
5465         if(this.cls){
5466             cfg.cls = this.cls;
5467         }
5468         
5469         if(this.disabled){
5470             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5471         }
5472         
5473         if(this.active){
5474             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5475         }
5476         
5477         return cfg;
5478     },
5479     
5480     initEvents: function() {
5481         
5482         this.el.on('click', this.onClick, this);
5483         
5484     },
5485     onClick : function(e)
5486     {
5487         Roo.log('PaginationItem on click ');
5488         if(this.preventDefault){
5489             e.preventDefault();
5490         }
5491         
5492         if(this.disabled){
5493             return;
5494         }
5495         
5496         this.fireEvent('click', this, e);
5497     }
5498    
5499 });
5500
5501  
5502
5503  /*
5504  * - LGPL
5505  *
5506  * slider
5507  * 
5508  */
5509
5510
5511 /**
5512  * @class Roo.bootstrap.Slider
5513  * @extends Roo.bootstrap.Component
5514  * Bootstrap Slider class
5515  *    
5516  * @constructor
5517  * Create a new Slider
5518  * @param {Object} config The config object
5519  */
5520
5521 Roo.bootstrap.Slider = function(config){
5522     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5523 };
5524
5525 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5526     
5527     getAutoCreate : function(){
5528         
5529         var cfg = {
5530             tag: 'div',
5531             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5532             cn: [
5533                 {
5534                     tag: 'a',
5535                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5536                 }
5537             ]
5538         };
5539         
5540         return cfg;
5541     }
5542    
5543 });
5544
5545  /*
5546  * Based on:
5547  * Ext JS Library 1.1.1
5548  * Copyright(c) 2006-2007, Ext JS, LLC.
5549  *
5550  * Originally Released Under LGPL - original licence link has changed is not relivant.
5551  *
5552  * Fork - LGPL
5553  * <script type="text/javascript">
5554  */
5555  
5556
5557 /**
5558  * @class Roo.grid.ColumnModel
5559  * @extends Roo.util.Observable
5560  * This is the default implementation of a ColumnModel used by the Grid. It defines
5561  * the columns in the grid.
5562  * <br>Usage:<br>
5563  <pre><code>
5564  var colModel = new Roo.grid.ColumnModel([
5565         {header: "Ticker", width: 60, sortable: true, locked: true},
5566         {header: "Company Name", width: 150, sortable: true},
5567         {header: "Market Cap.", width: 100, sortable: true},
5568         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5569         {header: "Employees", width: 100, sortable: true, resizable: false}
5570  ]);
5571  </code></pre>
5572  * <p>
5573  
5574  * The config options listed for this class are options which may appear in each
5575  * individual column definition.
5576  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5577  * @constructor
5578  * @param {Object} config An Array of column config objects. See this class's
5579  * config objects for details.
5580 */
5581 Roo.grid.ColumnModel = function(config){
5582         /**
5583      * The config passed into the constructor
5584      */
5585     this.config = config;
5586     this.lookup = {};
5587
5588     // if no id, create one
5589     // if the column does not have a dataIndex mapping,
5590     // map it to the order it is in the config
5591     for(var i = 0, len = config.length; i < len; i++){
5592         var c = config[i];
5593         if(typeof c.dataIndex == "undefined"){
5594             c.dataIndex = i;
5595         }
5596         if(typeof c.renderer == "string"){
5597             c.renderer = Roo.util.Format[c.renderer];
5598         }
5599         if(typeof c.id == "undefined"){
5600             c.id = Roo.id();
5601         }
5602         if(c.editor && c.editor.xtype){
5603             c.editor  = Roo.factory(c.editor, Roo.grid);
5604         }
5605         if(c.editor && c.editor.isFormField){
5606             c.editor = new Roo.grid.GridEditor(c.editor);
5607         }
5608         this.lookup[c.id] = c;
5609     }
5610
5611     /**
5612      * The width of columns which have no width specified (defaults to 100)
5613      * @type Number
5614      */
5615     this.defaultWidth = 100;
5616
5617     /**
5618      * Default sortable of columns which have no sortable specified (defaults to false)
5619      * @type Boolean
5620      */
5621     this.defaultSortable = false;
5622
5623     this.addEvents({
5624         /**
5625              * @event widthchange
5626              * Fires when the width of a column changes.
5627              * @param {ColumnModel} this
5628              * @param {Number} columnIndex The column index
5629              * @param {Number} newWidth The new width
5630              */
5631             "widthchange": true,
5632         /**
5633              * @event headerchange
5634              * Fires when the text of a header changes.
5635              * @param {ColumnModel} this
5636              * @param {Number} columnIndex The column index
5637              * @param {Number} newText The new header text
5638              */
5639             "headerchange": true,
5640         /**
5641              * @event hiddenchange
5642              * Fires when a column is hidden or "unhidden".
5643              * @param {ColumnModel} this
5644              * @param {Number} columnIndex The column index
5645              * @param {Boolean} hidden true if hidden, false otherwise
5646              */
5647             "hiddenchange": true,
5648             /**
5649          * @event columnmoved
5650          * Fires when a column is moved.
5651          * @param {ColumnModel} this
5652          * @param {Number} oldIndex
5653          * @param {Number} newIndex
5654          */
5655         "columnmoved" : true,
5656         /**
5657          * @event columlockchange
5658          * Fires when a column's locked state is changed
5659          * @param {ColumnModel} this
5660          * @param {Number} colIndex
5661          * @param {Boolean} locked true if locked
5662          */
5663         "columnlockchange" : true
5664     });
5665     Roo.grid.ColumnModel.superclass.constructor.call(this);
5666 };
5667 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5668     /**
5669      * @cfg {String} header The header text to display in the Grid view.
5670      */
5671     /**
5672      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5673      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5674      * specified, the column's index is used as an index into the Record's data Array.
5675      */
5676     /**
5677      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5678      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5679      */
5680     /**
5681      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5682      * Defaults to the value of the {@link #defaultSortable} property.
5683      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5684      */
5685     /**
5686      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5687      */
5688     /**
5689      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5690      */
5691     /**
5692      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5693      */
5694     /**
5695      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5696      */
5697     /**
5698      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5699      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5700      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5701      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5702      */
5703        /**
5704      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5705      */
5706     /**
5707      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5708      */
5709     /**
5710      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5711      */
5712     /**
5713      * @cfg {String} cursor (Optional)
5714      */
5715     /**
5716      * @cfg {String} tooltip (Optional)
5717      */
5718     /**
5719      * @cfg {Number} xs (Optional)
5720      */
5721     /**
5722      * @cfg {Number} sm (Optional)
5723      */
5724     /**
5725      * @cfg {Number} md (Optional)
5726      */
5727     /**
5728      * @cfg {Number} lg (Optional)
5729      */
5730     /**
5731      * Returns the id of the column at the specified index.
5732      * @param {Number} index The column index
5733      * @return {String} the id
5734      */
5735     getColumnId : function(index){
5736         return this.config[index].id;
5737     },
5738
5739     /**
5740      * Returns the column for a specified id.
5741      * @param {String} id The column id
5742      * @return {Object} the column
5743      */
5744     getColumnById : function(id){
5745         return this.lookup[id];
5746     },
5747
5748     
5749     /**
5750      * Returns the column for a specified dataIndex.
5751      * @param {String} dataIndex The column dataIndex
5752      * @return {Object|Boolean} the column or false if not found
5753      */
5754     getColumnByDataIndex: function(dataIndex){
5755         var index = this.findColumnIndex(dataIndex);
5756         return index > -1 ? this.config[index] : false;
5757     },
5758     
5759     /**
5760      * Returns the index for a specified column id.
5761      * @param {String} id The column id
5762      * @return {Number} the index, or -1 if not found
5763      */
5764     getIndexById : function(id){
5765         for(var i = 0, len = this.config.length; i < len; i++){
5766             if(this.config[i].id == id){
5767                 return i;
5768             }
5769         }
5770         return -1;
5771     },
5772     
5773     /**
5774      * Returns the index for a specified column dataIndex.
5775      * @param {String} dataIndex The column dataIndex
5776      * @return {Number} the index, or -1 if not found
5777      */
5778     
5779     findColumnIndex : function(dataIndex){
5780         for(var i = 0, len = this.config.length; i < len; i++){
5781             if(this.config[i].dataIndex == dataIndex){
5782                 return i;
5783             }
5784         }
5785         return -1;
5786     },
5787     
5788     
5789     moveColumn : function(oldIndex, newIndex){
5790         var c = this.config[oldIndex];
5791         this.config.splice(oldIndex, 1);
5792         this.config.splice(newIndex, 0, c);
5793         this.dataMap = null;
5794         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5795     },
5796
5797     isLocked : function(colIndex){
5798         return this.config[colIndex].locked === true;
5799     },
5800
5801     setLocked : function(colIndex, value, suppressEvent){
5802         if(this.isLocked(colIndex) == value){
5803             return;
5804         }
5805         this.config[colIndex].locked = value;
5806         if(!suppressEvent){
5807             this.fireEvent("columnlockchange", this, colIndex, value);
5808         }
5809     },
5810
5811     getTotalLockedWidth : function(){
5812         var totalWidth = 0;
5813         for(var i = 0; i < this.config.length; i++){
5814             if(this.isLocked(i) && !this.isHidden(i)){
5815                 this.totalWidth += this.getColumnWidth(i);
5816             }
5817         }
5818         return totalWidth;
5819     },
5820
5821     getLockedCount : function(){
5822         for(var i = 0, len = this.config.length; i < len; i++){
5823             if(!this.isLocked(i)){
5824                 return i;
5825             }
5826         }
5827         
5828         return this.config.length;
5829     },
5830
5831     /**
5832      * Returns the number of columns.
5833      * @return {Number}
5834      */
5835     getColumnCount : function(visibleOnly){
5836         if(visibleOnly === true){
5837             var c = 0;
5838             for(var i = 0, len = this.config.length; i < len; i++){
5839                 if(!this.isHidden(i)){
5840                     c++;
5841                 }
5842             }
5843             return c;
5844         }
5845         return this.config.length;
5846     },
5847
5848     /**
5849      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5850      * @param {Function} fn
5851      * @param {Object} scope (optional)
5852      * @return {Array} result
5853      */
5854     getColumnsBy : function(fn, scope){
5855         var r = [];
5856         for(var i = 0, len = this.config.length; i < len; i++){
5857             var c = this.config[i];
5858             if(fn.call(scope||this, c, i) === true){
5859                 r[r.length] = c;
5860             }
5861         }
5862         return r;
5863     },
5864
5865     /**
5866      * Returns true if the specified column is sortable.
5867      * @param {Number} col The column index
5868      * @return {Boolean}
5869      */
5870     isSortable : function(col){
5871         if(typeof this.config[col].sortable == "undefined"){
5872             return this.defaultSortable;
5873         }
5874         return this.config[col].sortable;
5875     },
5876
5877     /**
5878      * Returns the rendering (formatting) function defined for the column.
5879      * @param {Number} col The column index.
5880      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5881      */
5882     getRenderer : function(col){
5883         if(!this.config[col].renderer){
5884             return Roo.grid.ColumnModel.defaultRenderer;
5885         }
5886         return this.config[col].renderer;
5887     },
5888
5889     /**
5890      * Sets the rendering (formatting) function for a column.
5891      * @param {Number} col The column index
5892      * @param {Function} fn The function to use to process the cell's raw data
5893      * to return HTML markup for the grid view. The render function is called with
5894      * the following parameters:<ul>
5895      * <li>Data value.</li>
5896      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5897      * <li>css A CSS style string to apply to the table cell.</li>
5898      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5899      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5900      * <li>Row index</li>
5901      * <li>Column index</li>
5902      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5903      */
5904     setRenderer : function(col, fn){
5905         this.config[col].renderer = fn;
5906     },
5907
5908     /**
5909      * Returns the width for the specified column.
5910      * @param {Number} col The column index
5911      * @return {Number}
5912      */
5913     getColumnWidth : function(col){
5914         return this.config[col].width * 1 || this.defaultWidth;
5915     },
5916
5917     /**
5918      * Sets the width for a column.
5919      * @param {Number} col The column index
5920      * @param {Number} width The new width
5921      */
5922     setColumnWidth : function(col, width, suppressEvent){
5923         this.config[col].width = width;
5924         this.totalWidth = null;
5925         if(!suppressEvent){
5926              this.fireEvent("widthchange", this, col, width);
5927         }
5928     },
5929
5930     /**
5931      * Returns the total width of all columns.
5932      * @param {Boolean} includeHidden True to include hidden column widths
5933      * @return {Number}
5934      */
5935     getTotalWidth : function(includeHidden){
5936         if(!this.totalWidth){
5937             this.totalWidth = 0;
5938             for(var i = 0, len = this.config.length; i < len; i++){
5939                 if(includeHidden || !this.isHidden(i)){
5940                     this.totalWidth += this.getColumnWidth(i);
5941                 }
5942             }
5943         }
5944         return this.totalWidth;
5945     },
5946
5947     /**
5948      * Returns the header for the specified column.
5949      * @param {Number} col The column index
5950      * @return {String}
5951      */
5952     getColumnHeader : function(col){
5953         return this.config[col].header;
5954     },
5955
5956     /**
5957      * Sets the header for a column.
5958      * @param {Number} col The column index
5959      * @param {String} header The new header
5960      */
5961     setColumnHeader : function(col, header){
5962         this.config[col].header = header;
5963         this.fireEvent("headerchange", this, col, header);
5964     },
5965
5966     /**
5967      * Returns the tooltip for the specified column.
5968      * @param {Number} col The column index
5969      * @return {String}
5970      */
5971     getColumnTooltip : function(col){
5972             return this.config[col].tooltip;
5973     },
5974     /**
5975      * Sets the tooltip for a column.
5976      * @param {Number} col The column index
5977      * @param {String} tooltip The new tooltip
5978      */
5979     setColumnTooltip : function(col, tooltip){
5980             this.config[col].tooltip = tooltip;
5981     },
5982
5983     /**
5984      * Returns the dataIndex for the specified column.
5985      * @param {Number} col The column index
5986      * @return {Number}
5987      */
5988     getDataIndex : function(col){
5989         return this.config[col].dataIndex;
5990     },
5991
5992     /**
5993      * Sets the dataIndex for a column.
5994      * @param {Number} col The column index
5995      * @param {Number} dataIndex The new dataIndex
5996      */
5997     setDataIndex : function(col, dataIndex){
5998         this.config[col].dataIndex = dataIndex;
5999     },
6000
6001     
6002     
6003     /**
6004      * Returns true if the cell is editable.
6005      * @param {Number} colIndex The column index
6006      * @param {Number} rowIndex The row index - this is nto actually used..?
6007      * @return {Boolean}
6008      */
6009     isCellEditable : function(colIndex, rowIndex){
6010         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6011     },
6012
6013     /**
6014      * Returns the editor defined for the cell/column.
6015      * return false or null to disable editing.
6016      * @param {Number} colIndex The column index
6017      * @param {Number} rowIndex The row index
6018      * @return {Object}
6019      */
6020     getCellEditor : function(colIndex, rowIndex){
6021         return this.config[colIndex].editor;
6022     },
6023
6024     /**
6025      * Sets if a column is editable.
6026      * @param {Number} col The column index
6027      * @param {Boolean} editable True if the column is editable
6028      */
6029     setEditable : function(col, editable){
6030         this.config[col].editable = editable;
6031     },
6032
6033
6034     /**
6035      * Returns true if the column is hidden.
6036      * @param {Number} colIndex The column index
6037      * @return {Boolean}
6038      */
6039     isHidden : function(colIndex){
6040         return this.config[colIndex].hidden;
6041     },
6042
6043
6044     /**
6045      * Returns true if the column width cannot be changed
6046      */
6047     isFixed : function(colIndex){
6048         return this.config[colIndex].fixed;
6049     },
6050
6051     /**
6052      * Returns true if the column can be resized
6053      * @return {Boolean}
6054      */
6055     isResizable : function(colIndex){
6056         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6057     },
6058     /**
6059      * Sets if a column is hidden.
6060      * @param {Number} colIndex The column index
6061      * @param {Boolean} hidden True if the column is hidden
6062      */
6063     setHidden : function(colIndex, hidden){
6064         this.config[colIndex].hidden = hidden;
6065         this.totalWidth = null;
6066         this.fireEvent("hiddenchange", this, colIndex, hidden);
6067     },
6068
6069     /**
6070      * Sets the editor for a column.
6071      * @param {Number} col The column index
6072      * @param {Object} editor The editor object
6073      */
6074     setEditor : function(col, editor){
6075         this.config[col].editor = editor;
6076     }
6077 });
6078
6079 Roo.grid.ColumnModel.defaultRenderer = function(value)
6080 {
6081     if(typeof value == "object") {
6082         return value;
6083     }
6084         if(typeof value == "string" && value.length < 1){
6085             return "&#160;";
6086         }
6087     
6088         return String.format("{0}", value);
6089 };
6090
6091 // Alias for backwards compatibility
6092 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6093 /*
6094  * Based on:
6095  * Ext JS Library 1.1.1
6096  * Copyright(c) 2006-2007, Ext JS, LLC.
6097  *
6098  * Originally Released Under LGPL - original licence link has changed is not relivant.
6099  *
6100  * Fork - LGPL
6101  * <script type="text/javascript">
6102  */
6103  
6104 /**
6105  * @class Roo.LoadMask
6106  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6107  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6108  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6109  * element's UpdateManager load indicator and will be destroyed after the initial load.
6110  * @constructor
6111  * Create a new LoadMask
6112  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6113  * @param {Object} config The config object
6114  */
6115 Roo.LoadMask = function(el, config){
6116     this.el = Roo.get(el);
6117     Roo.apply(this, config);
6118     if(this.store){
6119         this.store.on('beforeload', this.onBeforeLoad, this);
6120         this.store.on('load', this.onLoad, this);
6121         this.store.on('loadexception', this.onLoadException, this);
6122         this.removeMask = false;
6123     }else{
6124         var um = this.el.getUpdateManager();
6125         um.showLoadIndicator = false; // disable the default indicator
6126         um.on('beforeupdate', this.onBeforeLoad, this);
6127         um.on('update', this.onLoad, this);
6128         um.on('failure', this.onLoad, this);
6129         this.removeMask = true;
6130     }
6131 };
6132
6133 Roo.LoadMask.prototype = {
6134     /**
6135      * @cfg {Boolean} removeMask
6136      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6137      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6138      */
6139     /**
6140      * @cfg {String} msg
6141      * The text to display in a centered loading message box (defaults to 'Loading...')
6142      */
6143     msg : 'Loading...',
6144     /**
6145      * @cfg {String} msgCls
6146      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6147      */
6148     msgCls : 'x-mask-loading',
6149
6150     /**
6151      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6152      * @type Boolean
6153      */
6154     disabled: false,
6155
6156     /**
6157      * Disables the mask to prevent it from being displayed
6158      */
6159     disable : function(){
6160        this.disabled = true;
6161     },
6162
6163     /**
6164      * Enables the mask so that it can be displayed
6165      */
6166     enable : function(){
6167         this.disabled = false;
6168     },
6169     
6170     onLoadException : function()
6171     {
6172         Roo.log(arguments);
6173         
6174         if (typeof(arguments[3]) != 'undefined') {
6175             Roo.MessageBox.alert("Error loading",arguments[3]);
6176         } 
6177         /*
6178         try {
6179             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6180                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6181             }   
6182         } catch(e) {
6183             
6184         }
6185         */
6186     
6187         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6188     },
6189     // private
6190     onLoad : function()
6191     {
6192         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6193     },
6194
6195     // private
6196     onBeforeLoad : function(){
6197         if(!this.disabled){
6198             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6199         }
6200     },
6201
6202     // private
6203     destroy : function(){
6204         if(this.store){
6205             this.store.un('beforeload', this.onBeforeLoad, this);
6206             this.store.un('load', this.onLoad, this);
6207             this.store.un('loadexception', this.onLoadException, this);
6208         }else{
6209             var um = this.el.getUpdateManager();
6210             um.un('beforeupdate', this.onBeforeLoad, this);
6211             um.un('update', this.onLoad, this);
6212             um.un('failure', this.onLoad, this);
6213         }
6214     }
6215 };/*
6216  * - LGPL
6217  *
6218  * table
6219  * 
6220  */
6221
6222 /**
6223  * @class Roo.bootstrap.Table
6224  * @extends Roo.bootstrap.Component
6225  * Bootstrap Table class
6226  * @cfg {String} cls table class
6227  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6228  * @cfg {String} bgcolor Specifies the background color for a table
6229  * @cfg {Number} border Specifies whether the table cells should have borders or not
6230  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6231  * @cfg {Number} cellspacing Specifies the space between cells
6232  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6233  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6234  * @cfg {String} sortable Specifies that the table should be sortable
6235  * @cfg {String} summary Specifies a summary of the content of a table
6236  * @cfg {Number} width Specifies the width of a table
6237  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6238  * 
6239  * @cfg {boolean} striped Should the rows be alternative striped
6240  * @cfg {boolean} bordered Add borders to the table
6241  * @cfg {boolean} hover Add hover highlighting
6242  * @cfg {boolean} condensed Format condensed
6243  * @cfg {boolean} responsive Format condensed
6244  * @cfg {Boolean} loadMask (true|false) default false
6245  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6246  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6247  * @cfg {Boolean} rowSelection (true|false) default false
6248  * @cfg {Boolean} cellSelection (true|false) default false
6249  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6250  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6251  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6252  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6253  
6254  * 
6255  * @constructor
6256  * Create a new Table
6257  * @param {Object} config The config object
6258  */
6259
6260 Roo.bootstrap.Table = function(config){
6261     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6262     
6263   
6264     
6265     // BC...
6266     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6267     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6268     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6269     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6270     
6271     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6272     if (this.sm) {
6273         this.sm.grid = this;
6274         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6275         this.sm = this.selModel;
6276         this.sm.xmodule = this.xmodule || false;
6277     }
6278     
6279     if (this.cm && typeof(this.cm.config) == 'undefined') {
6280         this.colModel = new Roo.grid.ColumnModel(this.cm);
6281         this.cm = this.colModel;
6282         this.cm.xmodule = this.xmodule || false;
6283     }
6284     if (this.store) {
6285         this.store= Roo.factory(this.store, Roo.data);
6286         this.ds = this.store;
6287         this.ds.xmodule = this.xmodule || false;
6288          
6289     }
6290     if (this.footer && this.store) {
6291         this.footer.dataSource = this.ds;
6292         this.footer = Roo.factory(this.footer);
6293     }
6294     
6295     /** @private */
6296     this.addEvents({
6297         /**
6298          * @event cellclick
6299          * Fires when a cell is clicked
6300          * @param {Roo.bootstrap.Table} this
6301          * @param {Roo.Element} el
6302          * @param {Number} rowIndex
6303          * @param {Number} columnIndex
6304          * @param {Roo.EventObject} e
6305          */
6306         "cellclick" : true,
6307         /**
6308          * @event celldblclick
6309          * Fires when a cell is double clicked
6310          * @param {Roo.bootstrap.Table} this
6311          * @param {Roo.Element} el
6312          * @param {Number} rowIndex
6313          * @param {Number} columnIndex
6314          * @param {Roo.EventObject} e
6315          */
6316         "celldblclick" : true,
6317         /**
6318          * @event rowclick
6319          * Fires when a row is clicked
6320          * @param {Roo.bootstrap.Table} this
6321          * @param {Roo.Element} el
6322          * @param {Number} rowIndex
6323          * @param {Roo.EventObject} e
6324          */
6325         "rowclick" : true,
6326         /**
6327          * @event rowdblclick
6328          * Fires when a row is double clicked
6329          * @param {Roo.bootstrap.Table} this
6330          * @param {Roo.Element} el
6331          * @param {Number} rowIndex
6332          * @param {Roo.EventObject} e
6333          */
6334         "rowdblclick" : true,
6335         /**
6336          * @event mouseover
6337          * Fires when a mouseover occur
6338          * @param {Roo.bootstrap.Table} this
6339          * @param {Roo.Element} el
6340          * @param {Number} rowIndex
6341          * @param {Number} columnIndex
6342          * @param {Roo.EventObject} e
6343          */
6344         "mouseover" : true,
6345         /**
6346          * @event mouseout
6347          * Fires when a mouseout occur
6348          * @param {Roo.bootstrap.Table} this
6349          * @param {Roo.Element} el
6350          * @param {Number} rowIndex
6351          * @param {Number} columnIndex
6352          * @param {Roo.EventObject} e
6353          */
6354         "mouseout" : true,
6355         /**
6356          * @event rowclass
6357          * Fires when a row is rendered, so you can change add a style to it.
6358          * @param {Roo.bootstrap.Table} this
6359          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6360          */
6361         'rowclass' : true,
6362           /**
6363          * @event rowsrendered
6364          * Fires when all the  rows have been rendered
6365          * @param {Roo.bootstrap.Table} this
6366          */
6367         'rowsrendered' : true,
6368         /**
6369          * @event contextmenu
6370          * The raw contextmenu event for the entire grid.
6371          * @param {Roo.EventObject} e
6372          */
6373         "contextmenu" : true,
6374         /**
6375          * @event rowcontextmenu
6376          * Fires when a row is right clicked
6377          * @param {Roo.bootstrap.Table} this
6378          * @param {Number} rowIndex
6379          * @param {Roo.EventObject} e
6380          */
6381         "rowcontextmenu" : true,
6382         /**
6383          * @event cellcontextmenu
6384          * Fires when a cell is right clicked
6385          * @param {Roo.bootstrap.Table} this
6386          * @param {Number} rowIndex
6387          * @param {Number} cellIndex
6388          * @param {Roo.EventObject} e
6389          */
6390          "cellcontextmenu" : true,
6391          /**
6392          * @event headercontextmenu
6393          * Fires when a header is right clicked
6394          * @param {Roo.bootstrap.Table} this
6395          * @param {Number} columnIndex
6396          * @param {Roo.EventObject} e
6397          */
6398         "headercontextmenu" : true
6399     });
6400 };
6401
6402 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6403     
6404     cls: false,
6405     align: false,
6406     bgcolor: false,
6407     border: false,
6408     cellpadding: false,
6409     cellspacing: false,
6410     frame: false,
6411     rules: false,
6412     sortable: false,
6413     summary: false,
6414     width: false,
6415     striped : false,
6416     scrollBody : false,
6417     bordered: false,
6418     hover:  false,
6419     condensed : false,
6420     responsive : false,
6421     sm : false,
6422     cm : false,
6423     store : false,
6424     loadMask : false,
6425     footerShow : true,
6426     headerShow : true,
6427   
6428     rowSelection : false,
6429     cellSelection : false,
6430     layout : false,
6431     
6432     // Roo.Element - the tbody
6433     mainBody: false,
6434     // Roo.Element - thead element
6435     mainHead: false,
6436     
6437     container: false, // used by gridpanel...
6438     
6439     lazyLoad : false,
6440     
6441     CSS : Roo.util.CSS,
6442     
6443     auto_hide_footer : false,
6444     
6445     getAutoCreate : function()
6446     {
6447         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6448         
6449         cfg = {
6450             tag: 'table',
6451             cls : 'table',
6452             cn : []
6453         };
6454         if (this.scrollBody) {
6455             cfg.cls += ' table-body-fixed';
6456         }    
6457         if (this.striped) {
6458             cfg.cls += ' table-striped';
6459         }
6460         
6461         if (this.hover) {
6462             cfg.cls += ' table-hover';
6463         }
6464         if (this.bordered) {
6465             cfg.cls += ' table-bordered';
6466         }
6467         if (this.condensed) {
6468             cfg.cls += ' table-condensed';
6469         }
6470         if (this.responsive) {
6471             cfg.cls += ' table-responsive';
6472         }
6473         
6474         if (this.cls) {
6475             cfg.cls+=  ' ' +this.cls;
6476         }
6477         
6478         // this lot should be simplifed...
6479         var _t = this;
6480         var cp = [
6481             'align',
6482             'bgcolor',
6483             'border',
6484             'cellpadding',
6485             'cellspacing',
6486             'frame',
6487             'rules',
6488             'sortable',
6489             'summary',
6490             'width'
6491         ].forEach(function(k) {
6492             if (_t[k]) {
6493                 cfg[k] = _t[k];
6494             }
6495         });
6496         
6497         
6498         if (this.layout) {
6499             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6500         }
6501         
6502         if(this.store || this.cm){
6503             if(this.headerShow){
6504                 cfg.cn.push(this.renderHeader());
6505             }
6506             
6507             cfg.cn.push(this.renderBody());
6508             
6509             if(this.footerShow){
6510                 cfg.cn.push(this.renderFooter());
6511             }
6512             // where does this come from?
6513             //cfg.cls+=  ' TableGrid';
6514         }
6515         
6516         return { cn : [ cfg ] };
6517     },
6518     
6519     initEvents : function()
6520     {   
6521         if(!this.store || !this.cm){
6522             return;
6523         }
6524         if (this.selModel) {
6525             this.selModel.initEvents();
6526         }
6527         
6528         
6529         //Roo.log('initEvents with ds!!!!');
6530         
6531         this.mainBody = this.el.select('tbody', true).first();
6532         this.mainHead = this.el.select('thead', true).first();
6533         this.mainFoot = this.el.select('tfoot', true).first();
6534         
6535         
6536         
6537         var _this = this;
6538         
6539         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6540             e.on('click', _this.sort, _this);
6541         });
6542         
6543         this.mainBody.on("click", this.onClick, this);
6544         this.mainBody.on("dblclick", this.onDblClick, this);
6545         
6546         // why is this done????? = it breaks dialogs??
6547         //this.parent().el.setStyle('position', 'relative');
6548         
6549         
6550         if (this.footer) {
6551             this.footer.parentId = this.id;
6552             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6553             
6554             if(this.lazyLoad){
6555                 this.el.select('tfoot tr td').first().addClass('hide');
6556             }
6557         } 
6558         
6559         if(this.loadMask) {
6560             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6561         }
6562         
6563         this.store.on('load', this.onLoad, this);
6564         this.store.on('beforeload', this.onBeforeLoad, this);
6565         this.store.on('update', this.onUpdate, this);
6566         this.store.on('add', this.onAdd, this);
6567         this.store.on("clear", this.clear, this);
6568         
6569         this.el.on("contextmenu", this.onContextMenu, this);
6570         
6571         this.mainBody.on('scroll', this.onBodyScroll, this);
6572         
6573         this.cm.on("headerchange", this.onHeaderChange, this);
6574         
6575         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6576         
6577     },
6578     
6579     onContextMenu : function(e, t)
6580     {
6581         this.processEvent("contextmenu", e);
6582     },
6583     
6584     processEvent : function(name, e)
6585     {
6586         if (name != 'touchstart' ) {
6587             this.fireEvent(name, e);    
6588         }
6589         
6590         var t = e.getTarget();
6591         
6592         var cell = Roo.get(t);
6593         
6594         if(!cell){
6595             return;
6596         }
6597         
6598         if(cell.findParent('tfoot', false, true)){
6599             return;
6600         }
6601         
6602         if(cell.findParent('thead', false, true)){
6603             
6604             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6605                 cell = Roo.get(t).findParent('th', false, true);
6606                 if (!cell) {
6607                     Roo.log("failed to find th in thead?");
6608                     Roo.log(e.getTarget());
6609                     return;
6610                 }
6611             }
6612             
6613             var cellIndex = cell.dom.cellIndex;
6614             
6615             var ename = name == 'touchstart' ? 'click' : name;
6616             this.fireEvent("header" + ename, this, cellIndex, e);
6617             
6618             return;
6619         }
6620         
6621         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6622             cell = Roo.get(t).findParent('td', false, true);
6623             if (!cell) {
6624                 Roo.log("failed to find th in tbody?");
6625                 Roo.log(e.getTarget());
6626                 return;
6627             }
6628         }
6629         
6630         var row = cell.findParent('tr', false, true);
6631         var cellIndex = cell.dom.cellIndex;
6632         var rowIndex = row.dom.rowIndex - 1;
6633         
6634         if(row !== false){
6635             
6636             this.fireEvent("row" + name, this, rowIndex, e);
6637             
6638             if(cell !== false){
6639             
6640                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6641             }
6642         }
6643         
6644     },
6645     
6646     onMouseover : function(e, el)
6647     {
6648         var cell = Roo.get(el);
6649         
6650         if(!cell){
6651             return;
6652         }
6653         
6654         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6655             cell = cell.findParent('td', false, true);
6656         }
6657         
6658         var row = cell.findParent('tr', false, true);
6659         var cellIndex = cell.dom.cellIndex;
6660         var rowIndex = row.dom.rowIndex - 1; // start from 0
6661         
6662         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6663         
6664     },
6665     
6666     onMouseout : function(e, el)
6667     {
6668         var cell = Roo.get(el);
6669         
6670         if(!cell){
6671             return;
6672         }
6673         
6674         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6675             cell = cell.findParent('td', false, true);
6676         }
6677         
6678         var row = cell.findParent('tr', false, true);
6679         var cellIndex = cell.dom.cellIndex;
6680         var rowIndex = row.dom.rowIndex - 1; // start from 0
6681         
6682         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6683         
6684     },
6685     
6686     onClick : function(e, el)
6687     {
6688         var cell = Roo.get(el);
6689         
6690         if(!cell || (!this.cellSelection && !this.rowSelection)){
6691             return;
6692         }
6693         
6694         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6695             cell = cell.findParent('td', false, true);
6696         }
6697         
6698         if(!cell || typeof(cell) == 'undefined'){
6699             return;
6700         }
6701         
6702         var row = cell.findParent('tr', false, true);
6703         
6704         if(!row || typeof(row) == 'undefined'){
6705             return;
6706         }
6707         
6708         var cellIndex = cell.dom.cellIndex;
6709         var rowIndex = this.getRowIndex(row);
6710         
6711         // why??? - should these not be based on SelectionModel?
6712         if(this.cellSelection){
6713             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6714         }
6715         
6716         if(this.rowSelection){
6717             this.fireEvent('rowclick', this, row, rowIndex, e);
6718         }
6719         
6720         
6721     },
6722         
6723     onDblClick : function(e,el)
6724     {
6725         var cell = Roo.get(el);
6726         
6727         if(!cell || (!this.cellSelection && !this.rowSelection)){
6728             return;
6729         }
6730         
6731         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6732             cell = cell.findParent('td', false, true);
6733         }
6734         
6735         if(!cell || typeof(cell) == 'undefined'){
6736             return;
6737         }
6738         
6739         var row = cell.findParent('tr', false, true);
6740         
6741         if(!row || typeof(row) == 'undefined'){
6742             return;
6743         }
6744         
6745         var cellIndex = cell.dom.cellIndex;
6746         var rowIndex = this.getRowIndex(row);
6747         
6748         if(this.cellSelection){
6749             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6750         }
6751         
6752         if(this.rowSelection){
6753             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6754         }
6755     },
6756     
6757     sort : function(e,el)
6758     {
6759         var col = Roo.get(el);
6760         
6761         if(!col.hasClass('sortable')){
6762             return;
6763         }
6764         
6765         var sort = col.attr('sort');
6766         var dir = 'ASC';
6767         
6768         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6769             dir = 'DESC';
6770         }
6771         
6772         this.store.sortInfo = {field : sort, direction : dir};
6773         
6774         if (this.footer) {
6775             Roo.log("calling footer first");
6776             this.footer.onClick('first');
6777         } else {
6778         
6779             this.store.load({ params : { start : 0 } });
6780         }
6781     },
6782     
6783     renderHeader : function()
6784     {
6785         var header = {
6786             tag: 'thead',
6787             cn : []
6788         };
6789         
6790         var cm = this.cm;
6791         this.totalWidth = 0;
6792         
6793         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6794             
6795             var config = cm.config[i];
6796             
6797             var c = {
6798                 tag: 'th',
6799                 cls : 'x-hcol-' + i,
6800                 style : '',
6801                 html: cm.getColumnHeader(i)
6802             };
6803             
6804             var hh = '';
6805             
6806             if(typeof(config.sortable) != 'undefined' && config.sortable){
6807                 c.cls = 'sortable';
6808                 c.html = '<i class="glyphicon"></i>' + c.html;
6809             }
6810             
6811             if(typeof(config.lgHeader) != 'undefined'){
6812                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6813             }
6814             
6815             if(typeof(config.mdHeader) != 'undefined'){
6816                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6817             }
6818             
6819             if(typeof(config.smHeader) != 'undefined'){
6820                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6821             }
6822             
6823             if(typeof(config.xsHeader) != 'undefined'){
6824                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6825             }
6826             
6827             if(hh.length){
6828                 c.html = hh;
6829             }
6830             
6831             if(typeof(config.tooltip) != 'undefined'){
6832                 c.tooltip = config.tooltip;
6833             }
6834             
6835             if(typeof(config.colspan) != 'undefined'){
6836                 c.colspan = config.colspan;
6837             }
6838             
6839             if(typeof(config.hidden) != 'undefined' && config.hidden){
6840                 c.style += ' display:none;';
6841             }
6842             
6843             if(typeof(config.dataIndex) != 'undefined'){
6844                 c.sort = config.dataIndex;
6845             }
6846             
6847            
6848             
6849             if(typeof(config.align) != 'undefined' && config.align.length){
6850                 c.style += ' text-align:' + config.align + ';';
6851             }
6852             
6853             if(typeof(config.width) != 'undefined'){
6854                 c.style += ' width:' + config.width + 'px;';
6855                 this.totalWidth += config.width;
6856             } else {
6857                 this.totalWidth += 100; // assume minimum of 100 per column?
6858             }
6859             
6860             if(typeof(config.cls) != 'undefined'){
6861                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6862             }
6863             
6864             ['xs','sm','md','lg'].map(function(size){
6865                 
6866                 if(typeof(config[size]) == 'undefined'){
6867                     return;
6868                 }
6869                 
6870                 if (!config[size]) { // 0 = hidden
6871                     c.cls += ' hidden-' + size;
6872                     return;
6873                 }
6874                 
6875                 c.cls += ' col-' + size + '-' + config[size];
6876
6877             });
6878             
6879             header.cn.push(c)
6880         }
6881         
6882         return header;
6883     },
6884     
6885     renderBody : function()
6886     {
6887         var body = {
6888             tag: 'tbody',
6889             cn : [
6890                 {
6891                     tag: 'tr',
6892                     cn : [
6893                         {
6894                             tag : 'td',
6895                             colspan :  this.cm.getColumnCount()
6896                         }
6897                     ]
6898                 }
6899             ]
6900         };
6901         
6902         return body;
6903     },
6904     
6905     renderFooter : function()
6906     {
6907         var footer = {
6908             tag: 'tfoot',
6909             cn : [
6910                 {
6911                     tag: 'tr',
6912                     cn : [
6913                         {
6914                             tag : 'td',
6915                             colspan :  this.cm.getColumnCount()
6916                         }
6917                     ]
6918                 }
6919             ]
6920         };
6921         
6922         return footer;
6923     },
6924     
6925     
6926     
6927     onLoad : function()
6928     {
6929 //        Roo.log('ds onload');
6930         this.clear();
6931         
6932         var _this = this;
6933         var cm = this.cm;
6934         var ds = this.store;
6935         
6936         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6937             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6938             if (_this.store.sortInfo) {
6939                     
6940                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6941                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6942                 }
6943                 
6944                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6945                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6946                 }
6947             }
6948         });
6949         
6950         var tbody =  this.mainBody;
6951               
6952         if(ds.getCount() > 0){
6953             ds.data.each(function(d,rowIndex){
6954                 var row =  this.renderRow(cm, ds, rowIndex);
6955                 
6956                 tbody.createChild(row);
6957                 
6958                 var _this = this;
6959                 
6960                 if(row.cellObjects.length){
6961                     Roo.each(row.cellObjects, function(r){
6962                         _this.renderCellObject(r);
6963                     })
6964                 }
6965                 
6966             }, this);
6967         }
6968         
6969         var tfoot = this.el.select('tfoot', true).first();
6970         
6971         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6972             
6973             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6974             
6975             var total = this.ds.getTotalCount();
6976             
6977             if(this.footer.pageSize < total){
6978                 this.mainFoot.show();
6979             }
6980         }
6981         
6982         Roo.each(this.el.select('tbody td', true).elements, function(e){
6983             e.on('mouseover', _this.onMouseover, _this);
6984         });
6985         
6986         Roo.each(this.el.select('tbody td', true).elements, function(e){
6987             e.on('mouseout', _this.onMouseout, _this);
6988         });
6989         this.fireEvent('rowsrendered', this);
6990         
6991         this.autoSize();
6992     },
6993     
6994     
6995     onUpdate : function(ds,record)
6996     {
6997         this.refreshRow(record);
6998         this.autoSize();
6999     },
7000     
7001     onRemove : function(ds, record, index, isUpdate){
7002         if(isUpdate !== true){
7003             this.fireEvent("beforerowremoved", this, index, record);
7004         }
7005         var bt = this.mainBody.dom;
7006         
7007         var rows = this.el.select('tbody > tr', true).elements;
7008         
7009         if(typeof(rows[index]) != 'undefined'){
7010             bt.removeChild(rows[index].dom);
7011         }
7012         
7013 //        if(bt.rows[index]){
7014 //            bt.removeChild(bt.rows[index]);
7015 //        }
7016         
7017         if(isUpdate !== true){
7018             //this.stripeRows(index);
7019             //this.syncRowHeights(index, index);
7020             //this.layout();
7021             this.fireEvent("rowremoved", this, index, record);
7022         }
7023     },
7024     
7025     onAdd : function(ds, records, rowIndex)
7026     {
7027         //Roo.log('on Add called');
7028         // - note this does not handle multiple adding very well..
7029         var bt = this.mainBody.dom;
7030         for (var i =0 ; i < records.length;i++) {
7031             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7032             //Roo.log(records[i]);
7033             //Roo.log(this.store.getAt(rowIndex+i));
7034             this.insertRow(this.store, rowIndex + i, false);
7035             return;
7036         }
7037         
7038     },
7039     
7040     
7041     refreshRow : function(record){
7042         var ds = this.store, index;
7043         if(typeof record == 'number'){
7044             index = record;
7045             record = ds.getAt(index);
7046         }else{
7047             index = ds.indexOf(record);
7048         }
7049         this.insertRow(ds, index, true);
7050         this.autoSize();
7051         this.onRemove(ds, record, index+1, true);
7052         this.autoSize();
7053         //this.syncRowHeights(index, index);
7054         //this.layout();
7055         this.fireEvent("rowupdated", this, index, record);
7056     },
7057     
7058     insertRow : function(dm, rowIndex, isUpdate){
7059         
7060         if(!isUpdate){
7061             this.fireEvent("beforerowsinserted", this, rowIndex);
7062         }
7063             //var s = this.getScrollState();
7064         var row = this.renderRow(this.cm, this.store, rowIndex);
7065         // insert before rowIndex..
7066         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7067         
7068         var _this = this;
7069                 
7070         if(row.cellObjects.length){
7071             Roo.each(row.cellObjects, function(r){
7072                 _this.renderCellObject(r);
7073             })
7074         }
7075             
7076         if(!isUpdate){
7077             this.fireEvent("rowsinserted", this, rowIndex);
7078             //this.syncRowHeights(firstRow, lastRow);
7079             //this.stripeRows(firstRow);
7080             //this.layout();
7081         }
7082         
7083     },
7084     
7085     
7086     getRowDom : function(rowIndex)
7087     {
7088         var rows = this.el.select('tbody > tr', true).elements;
7089         
7090         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7091         
7092     },
7093     // returns the object tree for a tr..
7094   
7095     
7096     renderRow : function(cm, ds, rowIndex) 
7097     {
7098         var d = ds.getAt(rowIndex);
7099         
7100         var row = {
7101             tag : 'tr',
7102             cls : 'x-row-' + rowIndex,
7103             cn : []
7104         };
7105             
7106         var cellObjects = [];
7107         
7108         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7109             var config = cm.config[i];
7110             
7111             var renderer = cm.getRenderer(i);
7112             var value = '';
7113             var id = false;
7114             
7115             if(typeof(renderer) !== 'undefined'){
7116                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7117             }
7118             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7119             // and are rendered into the cells after the row is rendered - using the id for the element.
7120             
7121             if(typeof(value) === 'object'){
7122                 id = Roo.id();
7123                 cellObjects.push({
7124                     container : id,
7125                     cfg : value 
7126                 })
7127             }
7128             
7129             var rowcfg = {
7130                 record: d,
7131                 rowIndex : rowIndex,
7132                 colIndex : i,
7133                 rowClass : ''
7134             };
7135
7136             this.fireEvent('rowclass', this, rowcfg);
7137             
7138             var td = {
7139                 tag: 'td',
7140                 cls : rowcfg.rowClass + ' x-col-' + i,
7141                 style: '',
7142                 html: (typeof(value) === 'object') ? '' : value
7143             };
7144             
7145             if (id) {
7146                 td.id = id;
7147             }
7148             
7149             if(typeof(config.colspan) != 'undefined'){
7150                 td.colspan = config.colspan;
7151             }
7152             
7153             if(typeof(config.hidden) != 'undefined' && config.hidden){
7154                 td.style += ' display:none;';
7155             }
7156             
7157             if(typeof(config.align) != 'undefined' && config.align.length){
7158                 td.style += ' text-align:' + config.align + ';';
7159             }
7160             if(typeof(config.valign) != 'undefined' && config.valign.length){
7161                 td.style += ' vertical-align:' + config.valign + ';';
7162             }
7163             
7164             if(typeof(config.width) != 'undefined'){
7165                 td.style += ' width:' +  config.width + 'px;';
7166             }
7167             
7168             if(typeof(config.cursor) != 'undefined'){
7169                 td.style += ' cursor:' +  config.cursor + ';';
7170             }
7171             
7172             if(typeof(config.cls) != 'undefined'){
7173                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7174             }
7175             
7176             ['xs','sm','md','lg'].map(function(size){
7177                 
7178                 if(typeof(config[size]) == 'undefined'){
7179                     return;
7180                 }
7181                 
7182                 if (!config[size]) { // 0 = hidden
7183                     td.cls += ' hidden-' + size;
7184                     return;
7185                 }
7186                 
7187                 td.cls += ' col-' + size + '-' + config[size];
7188
7189             });
7190             
7191             row.cn.push(td);
7192            
7193         }
7194         
7195         row.cellObjects = cellObjects;
7196         
7197         return row;
7198           
7199     },
7200     
7201     
7202     
7203     onBeforeLoad : function()
7204     {
7205         
7206     },
7207      /**
7208      * Remove all rows
7209      */
7210     clear : function()
7211     {
7212         this.el.select('tbody', true).first().dom.innerHTML = '';
7213     },
7214     /**
7215      * Show or hide a row.
7216      * @param {Number} rowIndex to show or hide
7217      * @param {Boolean} state hide
7218      */
7219     setRowVisibility : function(rowIndex, state)
7220     {
7221         var bt = this.mainBody.dom;
7222         
7223         var rows = this.el.select('tbody > tr', true).elements;
7224         
7225         if(typeof(rows[rowIndex]) == 'undefined'){
7226             return;
7227         }
7228         rows[rowIndex].dom.style.display = state ? '' : 'none';
7229     },
7230     
7231     
7232     getSelectionModel : function(){
7233         if(!this.selModel){
7234             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7235         }
7236         return this.selModel;
7237     },
7238     /*
7239      * Render the Roo.bootstrap object from renderder
7240      */
7241     renderCellObject : function(r)
7242     {
7243         var _this = this;
7244         
7245         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7246         
7247         var t = r.cfg.render(r.container);
7248         
7249         if(r.cfg.cn){
7250             Roo.each(r.cfg.cn, function(c){
7251                 var child = {
7252                     container: t.getChildContainer(),
7253                     cfg: c
7254                 };
7255                 _this.renderCellObject(child);
7256             })
7257         }
7258     },
7259     
7260     getRowIndex : function(row)
7261     {
7262         var rowIndex = -1;
7263         
7264         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7265             if(el != row){
7266                 return;
7267             }
7268             
7269             rowIndex = index;
7270         });
7271         
7272         return rowIndex;
7273     },
7274      /**
7275      * Returns the grid's underlying element = used by panel.Grid
7276      * @return {Element} The element
7277      */
7278     getGridEl : function(){
7279         return this.el;
7280     },
7281      /**
7282      * Forces a resize - used by panel.Grid
7283      * @return {Element} The element
7284      */
7285     autoSize : function()
7286     {
7287         //var ctr = Roo.get(this.container.dom.parentElement);
7288         var ctr = Roo.get(this.el.dom);
7289         
7290         var thd = this.getGridEl().select('thead',true).first();
7291         var tbd = this.getGridEl().select('tbody', true).first();
7292         var tfd = this.getGridEl().select('tfoot', true).first();
7293         
7294         var cw = ctr.getWidth();
7295         
7296         if (tbd) {
7297             
7298             tbd.setSize(ctr.getWidth(),
7299                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7300             );
7301             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7302             cw -= barsize;
7303         }
7304         cw = Math.max(cw, this.totalWidth);
7305         this.getGridEl().select('tr',true).setWidth(cw);
7306         // resize 'expandable coloumn?
7307         
7308         return; // we doe not have a view in this design..
7309         
7310     },
7311     onBodyScroll: function()
7312     {
7313         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7314         if(this.mainHead){
7315             this.mainHead.setStyle({
7316                 'position' : 'relative',
7317                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7318             });
7319         }
7320         
7321         if(this.lazyLoad){
7322             
7323             var scrollHeight = this.mainBody.dom.scrollHeight;
7324             
7325             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7326             
7327             var height = this.mainBody.getHeight();
7328             
7329             if(scrollHeight - height == scrollTop) {
7330                 
7331                 var total = this.ds.getTotalCount();
7332                 
7333                 if(this.footer.cursor + this.footer.pageSize < total){
7334                     
7335                     this.footer.ds.load({
7336                         params : {
7337                             start : this.footer.cursor + this.footer.pageSize,
7338                             limit : this.footer.pageSize
7339                         },
7340                         add : true
7341                     });
7342                 }
7343             }
7344             
7345         }
7346     },
7347     
7348     onHeaderChange : function()
7349     {
7350         var header = this.renderHeader();
7351         var table = this.el.select('table', true).first();
7352         
7353         this.mainHead.remove();
7354         this.mainHead = table.createChild(header, this.mainBody, false);
7355     },
7356     
7357     onHiddenChange : function(colModel, colIndex, hidden)
7358     {
7359         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7360         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7361         
7362         this.CSS.updateRule(thSelector, "display", "");
7363         this.CSS.updateRule(tdSelector, "display", "");
7364         
7365         if(hidden){
7366             this.CSS.updateRule(thSelector, "display", "none");
7367             this.CSS.updateRule(tdSelector, "display", "none");
7368         }
7369         
7370         this.onHeaderChange();
7371         this.onLoad();
7372     },
7373     
7374     setColumnWidth: function(col_index, width)
7375     {
7376         // width = "md-2 xs-2..."
7377         if(!this.colModel.config[col_index]) {
7378             return;
7379         }
7380         
7381         var w = width.split(" ");
7382         
7383         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7384         
7385         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7386         
7387         
7388         for(var j = 0; j < w.length; j++) {
7389             
7390             if(!w[j]) {
7391                 continue;
7392             }
7393             
7394             var size_cls = w[j].split("-");
7395             
7396             if(!Number.isInteger(size_cls[1] * 1)) {
7397                 continue;
7398             }
7399             
7400             if(!this.colModel.config[col_index][size_cls[0]]) {
7401                 continue;
7402             }
7403             
7404             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7405                 continue;
7406             }
7407             
7408             h_row[0].classList.replace(
7409                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7410                 "col-"+size_cls[0]+"-"+size_cls[1]
7411             );
7412             
7413             for(var i = 0; i < rows.length; i++) {
7414                 
7415                 var size_cls = w[j].split("-");
7416                 
7417                 if(!Number.isInteger(size_cls[1] * 1)) {
7418                     continue;
7419                 }
7420                 
7421                 if(!this.colModel.config[col_index][size_cls[0]]) {
7422                     continue;
7423                 }
7424                 
7425                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7426                     continue;
7427                 }
7428                 
7429                 rows[i].classList.replace(
7430                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7431                     "col-"+size_cls[0]+"-"+size_cls[1]
7432                 );
7433             }
7434             
7435             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7436         }
7437     }
7438 });
7439
7440  
7441
7442  /*
7443  * - LGPL
7444  *
7445  * table cell
7446  * 
7447  */
7448
7449 /**
7450  * @class Roo.bootstrap.TableCell
7451  * @extends Roo.bootstrap.Component
7452  * Bootstrap TableCell class
7453  * @cfg {String} html cell contain text
7454  * @cfg {String} cls cell class
7455  * @cfg {String} tag cell tag (td|th) default td
7456  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7457  * @cfg {String} align Aligns the content in a cell
7458  * @cfg {String} axis Categorizes cells
7459  * @cfg {String} bgcolor Specifies the background color of a cell
7460  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7461  * @cfg {Number} colspan Specifies the number of columns a cell should span
7462  * @cfg {String} headers Specifies one or more header cells a cell is related to
7463  * @cfg {Number} height Sets the height of a cell
7464  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7465  * @cfg {Number} rowspan Sets the number of rows a cell should span
7466  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7467  * @cfg {String} valign Vertical aligns the content in a cell
7468  * @cfg {Number} width Specifies the width of a cell
7469  * 
7470  * @constructor
7471  * Create a new TableCell
7472  * @param {Object} config The config object
7473  */
7474
7475 Roo.bootstrap.TableCell = function(config){
7476     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7477 };
7478
7479 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7480     
7481     html: false,
7482     cls: false,
7483     tag: false,
7484     abbr: false,
7485     align: false,
7486     axis: false,
7487     bgcolor: false,
7488     charoff: false,
7489     colspan: false,
7490     headers: false,
7491     height: false,
7492     nowrap: false,
7493     rowspan: false,
7494     scope: false,
7495     valign: false,
7496     width: false,
7497     
7498     
7499     getAutoCreate : function(){
7500         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7501         
7502         cfg = {
7503             tag: 'td'
7504         };
7505         
7506         if(this.tag){
7507             cfg.tag = this.tag;
7508         }
7509         
7510         if (this.html) {
7511             cfg.html=this.html
7512         }
7513         if (this.cls) {
7514             cfg.cls=this.cls
7515         }
7516         if (this.abbr) {
7517             cfg.abbr=this.abbr
7518         }
7519         if (this.align) {
7520             cfg.align=this.align
7521         }
7522         if (this.axis) {
7523             cfg.axis=this.axis
7524         }
7525         if (this.bgcolor) {
7526             cfg.bgcolor=this.bgcolor
7527         }
7528         if (this.charoff) {
7529             cfg.charoff=this.charoff
7530         }
7531         if (this.colspan) {
7532             cfg.colspan=this.colspan
7533         }
7534         if (this.headers) {
7535             cfg.headers=this.headers
7536         }
7537         if (this.height) {
7538             cfg.height=this.height
7539         }
7540         if (this.nowrap) {
7541             cfg.nowrap=this.nowrap
7542         }
7543         if (this.rowspan) {
7544             cfg.rowspan=this.rowspan
7545         }
7546         if (this.scope) {
7547             cfg.scope=this.scope
7548         }
7549         if (this.valign) {
7550             cfg.valign=this.valign
7551         }
7552         if (this.width) {
7553             cfg.width=this.width
7554         }
7555         
7556         
7557         return cfg;
7558     }
7559    
7560 });
7561
7562  
7563
7564  /*
7565  * - LGPL
7566  *
7567  * table row
7568  * 
7569  */
7570
7571 /**
7572  * @class Roo.bootstrap.TableRow
7573  * @extends Roo.bootstrap.Component
7574  * Bootstrap TableRow class
7575  * @cfg {String} cls row class
7576  * @cfg {String} align Aligns the content in a table row
7577  * @cfg {String} bgcolor Specifies a background color for a table row
7578  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7579  * @cfg {String} valign Vertical aligns the content in a table row
7580  * 
7581  * @constructor
7582  * Create a new TableRow
7583  * @param {Object} config The config object
7584  */
7585
7586 Roo.bootstrap.TableRow = function(config){
7587     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7588 };
7589
7590 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7591     
7592     cls: false,
7593     align: false,
7594     bgcolor: false,
7595     charoff: false,
7596     valign: false,
7597     
7598     getAutoCreate : function(){
7599         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7600         
7601         cfg = {
7602             tag: 'tr'
7603         };
7604             
7605         if(this.cls){
7606             cfg.cls = this.cls;
7607         }
7608         if(this.align){
7609             cfg.align = this.align;
7610         }
7611         if(this.bgcolor){
7612             cfg.bgcolor = this.bgcolor;
7613         }
7614         if(this.charoff){
7615             cfg.charoff = this.charoff;
7616         }
7617         if(this.valign){
7618             cfg.valign = this.valign;
7619         }
7620         
7621         return cfg;
7622     }
7623    
7624 });
7625
7626  
7627
7628  /*
7629  * - LGPL
7630  *
7631  * table body
7632  * 
7633  */
7634
7635 /**
7636  * @class Roo.bootstrap.TableBody
7637  * @extends Roo.bootstrap.Component
7638  * Bootstrap TableBody class
7639  * @cfg {String} cls element class
7640  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7641  * @cfg {String} align Aligns the content inside the element
7642  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7643  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7644  * 
7645  * @constructor
7646  * Create a new TableBody
7647  * @param {Object} config The config object
7648  */
7649
7650 Roo.bootstrap.TableBody = function(config){
7651     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7652 };
7653
7654 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7655     
7656     cls: false,
7657     tag: false,
7658     align: false,
7659     charoff: false,
7660     valign: false,
7661     
7662     getAutoCreate : function(){
7663         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7664         
7665         cfg = {
7666             tag: 'tbody'
7667         };
7668             
7669         if (this.cls) {
7670             cfg.cls=this.cls
7671         }
7672         if(this.tag){
7673             cfg.tag = this.tag;
7674         }
7675         
7676         if(this.align){
7677             cfg.align = this.align;
7678         }
7679         if(this.charoff){
7680             cfg.charoff = this.charoff;
7681         }
7682         if(this.valign){
7683             cfg.valign = this.valign;
7684         }
7685         
7686         return cfg;
7687     }
7688     
7689     
7690 //    initEvents : function()
7691 //    {
7692 //        
7693 //        if(!this.store){
7694 //            return;
7695 //        }
7696 //        
7697 //        this.store = Roo.factory(this.store, Roo.data);
7698 //        this.store.on('load', this.onLoad, this);
7699 //        
7700 //        this.store.load();
7701 //        
7702 //    },
7703 //    
7704 //    onLoad: function () 
7705 //    {   
7706 //        this.fireEvent('load', this);
7707 //    }
7708 //    
7709 //   
7710 });
7711
7712  
7713
7714  /*
7715  * Based on:
7716  * Ext JS Library 1.1.1
7717  * Copyright(c) 2006-2007, Ext JS, LLC.
7718  *
7719  * Originally Released Under LGPL - original licence link has changed is not relivant.
7720  *
7721  * Fork - LGPL
7722  * <script type="text/javascript">
7723  */
7724
7725 // as we use this in bootstrap.
7726 Roo.namespace('Roo.form');
7727  /**
7728  * @class Roo.form.Action
7729  * Internal Class used to handle form actions
7730  * @constructor
7731  * @param {Roo.form.BasicForm} el The form element or its id
7732  * @param {Object} config Configuration options
7733  */
7734
7735  
7736  
7737 // define the action interface
7738 Roo.form.Action = function(form, options){
7739     this.form = form;
7740     this.options = options || {};
7741 };
7742 /**
7743  * Client Validation Failed
7744  * @const 
7745  */
7746 Roo.form.Action.CLIENT_INVALID = 'client';
7747 /**
7748  * Server Validation Failed
7749  * @const 
7750  */
7751 Roo.form.Action.SERVER_INVALID = 'server';
7752  /**
7753  * Connect to Server Failed
7754  * @const 
7755  */
7756 Roo.form.Action.CONNECT_FAILURE = 'connect';
7757 /**
7758  * Reading Data from Server Failed
7759  * @const 
7760  */
7761 Roo.form.Action.LOAD_FAILURE = 'load';
7762
7763 Roo.form.Action.prototype = {
7764     type : 'default',
7765     failureType : undefined,
7766     response : undefined,
7767     result : undefined,
7768
7769     // interface method
7770     run : function(options){
7771
7772     },
7773
7774     // interface method
7775     success : function(response){
7776
7777     },
7778
7779     // interface method
7780     handleResponse : function(response){
7781
7782     },
7783
7784     // default connection failure
7785     failure : function(response){
7786         
7787         this.response = response;
7788         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7789         this.form.afterAction(this, false);
7790     },
7791
7792     processResponse : function(response){
7793         this.response = response;
7794         if(!response.responseText){
7795             return true;
7796         }
7797         this.result = this.handleResponse(response);
7798         return this.result;
7799     },
7800
7801     // utility functions used internally
7802     getUrl : function(appendParams){
7803         var url = this.options.url || this.form.url || this.form.el.dom.action;
7804         if(appendParams){
7805             var p = this.getParams();
7806             if(p){
7807                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7808             }
7809         }
7810         return url;
7811     },
7812
7813     getMethod : function(){
7814         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7815     },
7816
7817     getParams : function(){
7818         var bp = this.form.baseParams;
7819         var p = this.options.params;
7820         if(p){
7821             if(typeof p == "object"){
7822                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7823             }else if(typeof p == 'string' && bp){
7824                 p += '&' + Roo.urlEncode(bp);
7825             }
7826         }else if(bp){
7827             p = Roo.urlEncode(bp);
7828         }
7829         return p;
7830     },
7831
7832     createCallback : function(){
7833         return {
7834             success: this.success,
7835             failure: this.failure,
7836             scope: this,
7837             timeout: (this.form.timeout*1000),
7838             upload: this.form.fileUpload ? this.success : undefined
7839         };
7840     }
7841 };
7842
7843 Roo.form.Action.Submit = function(form, options){
7844     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7845 };
7846
7847 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7848     type : 'submit',
7849
7850     haveProgress : false,
7851     uploadComplete : false,
7852     
7853     // uploadProgress indicator.
7854     uploadProgress : function()
7855     {
7856         if (!this.form.progressUrl) {
7857             return;
7858         }
7859         
7860         if (!this.haveProgress) {
7861             Roo.MessageBox.progress("Uploading", "Uploading");
7862         }
7863         if (this.uploadComplete) {
7864            Roo.MessageBox.hide();
7865            return;
7866         }
7867         
7868         this.haveProgress = true;
7869    
7870         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7871         
7872         var c = new Roo.data.Connection();
7873         c.request({
7874             url : this.form.progressUrl,
7875             params: {
7876                 id : uid
7877             },
7878             method: 'GET',
7879             success : function(req){
7880                //console.log(data);
7881                 var rdata = false;
7882                 var edata;
7883                 try  {
7884                    rdata = Roo.decode(req.responseText)
7885                 } catch (e) {
7886                     Roo.log("Invalid data from server..");
7887                     Roo.log(edata);
7888                     return;
7889                 }
7890                 if (!rdata || !rdata.success) {
7891                     Roo.log(rdata);
7892                     Roo.MessageBox.alert(Roo.encode(rdata));
7893                     return;
7894                 }
7895                 var data = rdata.data;
7896                 
7897                 if (this.uploadComplete) {
7898                    Roo.MessageBox.hide();
7899                    return;
7900                 }
7901                    
7902                 if (data){
7903                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7904                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7905                     );
7906                 }
7907                 this.uploadProgress.defer(2000,this);
7908             },
7909        
7910             failure: function(data) {
7911                 Roo.log('progress url failed ');
7912                 Roo.log(data);
7913             },
7914             scope : this
7915         });
7916            
7917     },
7918     
7919     
7920     run : function()
7921     {
7922         // run get Values on the form, so it syncs any secondary forms.
7923         this.form.getValues();
7924         
7925         var o = this.options;
7926         var method = this.getMethod();
7927         var isPost = method == 'POST';
7928         if(o.clientValidation === false || this.form.isValid()){
7929             
7930             if (this.form.progressUrl) {
7931                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7932                     (new Date() * 1) + '' + Math.random());
7933                     
7934             } 
7935             
7936             
7937             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7938                 form:this.form.el.dom,
7939                 url:this.getUrl(!isPost),
7940                 method: method,
7941                 params:isPost ? this.getParams() : null,
7942                 isUpload: this.form.fileUpload,
7943                 formData : this.form.formData
7944             }));
7945             
7946             this.uploadProgress();
7947
7948         }else if (o.clientValidation !== false){ // client validation failed
7949             this.failureType = Roo.form.Action.CLIENT_INVALID;
7950             this.form.afterAction(this, false);
7951         }
7952     },
7953
7954     success : function(response)
7955     {
7956         this.uploadComplete= true;
7957         if (this.haveProgress) {
7958             Roo.MessageBox.hide();
7959         }
7960         
7961         
7962         var result = this.processResponse(response);
7963         if(result === true || result.success){
7964             this.form.afterAction(this, true);
7965             return;
7966         }
7967         if(result.errors){
7968             this.form.markInvalid(result.errors);
7969             this.failureType = Roo.form.Action.SERVER_INVALID;
7970         }
7971         this.form.afterAction(this, false);
7972     },
7973     failure : function(response)
7974     {
7975         this.uploadComplete= true;
7976         if (this.haveProgress) {
7977             Roo.MessageBox.hide();
7978         }
7979         
7980         this.response = response;
7981         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7982         this.form.afterAction(this, false);
7983     },
7984     
7985     handleResponse : function(response){
7986         if(this.form.errorReader){
7987             var rs = this.form.errorReader.read(response);
7988             var errors = [];
7989             if(rs.records){
7990                 for(var i = 0, len = rs.records.length; i < len; i++) {
7991                     var r = rs.records[i];
7992                     errors[i] = r.data;
7993                 }
7994             }
7995             if(errors.length < 1){
7996                 errors = null;
7997             }
7998             return {
7999                 success : rs.success,
8000                 errors : errors
8001             };
8002         }
8003         var ret = false;
8004         try {
8005             ret = Roo.decode(response.responseText);
8006         } catch (e) {
8007             ret = {
8008                 success: false,
8009                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8010                 errors : []
8011             };
8012         }
8013         return ret;
8014         
8015     }
8016 });
8017
8018
8019 Roo.form.Action.Load = function(form, options){
8020     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8021     this.reader = this.form.reader;
8022 };
8023
8024 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8025     type : 'load',
8026
8027     run : function(){
8028         
8029         Roo.Ajax.request(Roo.apply(
8030                 this.createCallback(), {
8031                     method:this.getMethod(),
8032                     url:this.getUrl(false),
8033                     params:this.getParams()
8034         }));
8035     },
8036
8037     success : function(response){
8038         
8039         var result = this.processResponse(response);
8040         if(result === true || !result.success || !result.data){
8041             this.failureType = Roo.form.Action.LOAD_FAILURE;
8042             this.form.afterAction(this, false);
8043             return;
8044         }
8045         this.form.clearInvalid();
8046         this.form.setValues(result.data);
8047         this.form.afterAction(this, true);
8048     },
8049
8050     handleResponse : function(response){
8051         if(this.form.reader){
8052             var rs = this.form.reader.read(response);
8053             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8054             return {
8055                 success : rs.success,
8056                 data : data
8057             };
8058         }
8059         return Roo.decode(response.responseText);
8060     }
8061 });
8062
8063 Roo.form.Action.ACTION_TYPES = {
8064     'load' : Roo.form.Action.Load,
8065     'submit' : Roo.form.Action.Submit
8066 };/*
8067  * - LGPL
8068  *
8069  * form
8070  *
8071  */
8072
8073 /**
8074  * @class Roo.bootstrap.Form
8075  * @extends Roo.bootstrap.Component
8076  * Bootstrap Form class
8077  * @cfg {String} method  GET | POST (default POST)
8078  * @cfg {String} labelAlign top | left (default top)
8079  * @cfg {String} align left  | right - for navbars
8080  * @cfg {Boolean} loadMask load mask when submit (default true)
8081
8082  *
8083  * @constructor
8084  * Create a new Form
8085  * @param {Object} config The config object
8086  */
8087
8088
8089 Roo.bootstrap.Form = function(config){
8090     
8091     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8092     
8093     Roo.bootstrap.Form.popover.apply();
8094     
8095     this.addEvents({
8096         /**
8097          * @event clientvalidation
8098          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8099          * @param {Form} this
8100          * @param {Boolean} valid true if the form has passed client-side validation
8101          */
8102         clientvalidation: true,
8103         /**
8104          * @event beforeaction
8105          * Fires before any action is performed. Return false to cancel the action.
8106          * @param {Form} this
8107          * @param {Action} action The action to be performed
8108          */
8109         beforeaction: true,
8110         /**
8111          * @event actionfailed
8112          * Fires when an action fails.
8113          * @param {Form} this
8114          * @param {Action} action The action that failed
8115          */
8116         actionfailed : true,
8117         /**
8118          * @event actioncomplete
8119          * Fires when an action is completed.
8120          * @param {Form} this
8121          * @param {Action} action The action that completed
8122          */
8123         actioncomplete : true
8124     });
8125 };
8126
8127 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8128
8129      /**
8130      * @cfg {String} method
8131      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8132      */
8133     method : 'POST',
8134     /**
8135      * @cfg {String} url
8136      * The URL to use for form actions if one isn't supplied in the action options.
8137      */
8138     /**
8139      * @cfg {Boolean} fileUpload
8140      * Set to true if this form is a file upload.
8141      */
8142
8143     /**
8144      * @cfg {Object} baseParams
8145      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8146      */
8147
8148     /**
8149      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8150      */
8151     timeout: 30,
8152     /**
8153      * @cfg {Sting} align (left|right) for navbar forms
8154      */
8155     align : 'left',
8156
8157     // private
8158     activeAction : null,
8159
8160     /**
8161      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8162      * element by passing it or its id or mask the form itself by passing in true.
8163      * @type Mixed
8164      */
8165     waitMsgTarget : false,
8166
8167     loadMask : true,
8168     
8169     /**
8170      * @cfg {Boolean} errorMask (true|false) default false
8171      */
8172     errorMask : false,
8173     
8174     /**
8175      * @cfg {Number} maskOffset Default 100
8176      */
8177     maskOffset : 100,
8178     
8179     /**
8180      * @cfg {Boolean} maskBody
8181      */
8182     maskBody : false,
8183
8184     getAutoCreate : function(){
8185
8186         var cfg = {
8187             tag: 'form',
8188             method : this.method || 'POST',
8189             id : this.id || Roo.id(),
8190             cls : ''
8191         };
8192         if (this.parent().xtype.match(/^Nav/)) {
8193             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8194
8195         }
8196
8197         if (this.labelAlign == 'left' ) {
8198             cfg.cls += ' form-horizontal';
8199         }
8200
8201
8202         return cfg;
8203     },
8204     initEvents : function()
8205     {
8206         this.el.on('submit', this.onSubmit, this);
8207         // this was added as random key presses on the form where triggering form submit.
8208         this.el.on('keypress', function(e) {
8209             if (e.getCharCode() != 13) {
8210                 return true;
8211             }
8212             // we might need to allow it for textareas.. and some other items.
8213             // check e.getTarget().
8214
8215             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8216                 return true;
8217             }
8218
8219             Roo.log("keypress blocked");
8220
8221             e.preventDefault();
8222             return false;
8223         });
8224         
8225     },
8226     // private
8227     onSubmit : function(e){
8228         e.stopEvent();
8229     },
8230
8231      /**
8232      * Returns true if client-side validation on the form is successful.
8233      * @return Boolean
8234      */
8235     isValid : function(){
8236         var items = this.getItems();
8237         var valid = true;
8238         var target = false;
8239         
8240         items.each(function(f){
8241             
8242             if(f.validate()){
8243                 return;
8244             }
8245             
8246             Roo.log('invalid field: ' + f.name);
8247             
8248             valid = false;
8249
8250             if(!target && f.el.isVisible(true)){
8251                 target = f;
8252             }
8253            
8254         });
8255         
8256         if(this.errorMask && !valid){
8257             Roo.bootstrap.Form.popover.mask(this, target);
8258         }
8259         
8260         return valid;
8261     },
8262     
8263     /**
8264      * Returns true if any fields in this form have changed since their original load.
8265      * @return Boolean
8266      */
8267     isDirty : function(){
8268         var dirty = false;
8269         var items = this.getItems();
8270         items.each(function(f){
8271            if(f.isDirty()){
8272                dirty = true;
8273                return false;
8274            }
8275            return true;
8276         });
8277         return dirty;
8278     },
8279      /**
8280      * Performs a predefined action (submit or load) or custom actions you define on this form.
8281      * @param {String} actionName The name of the action type
8282      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8283      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8284      * accept other config options):
8285      * <pre>
8286 Property          Type             Description
8287 ----------------  ---------------  ----------------------------------------------------------------------------------
8288 url               String           The url for the action (defaults to the form's url)
8289 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8290 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8291 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8292                                    validate the form on the client (defaults to false)
8293      * </pre>
8294      * @return {BasicForm} this
8295      */
8296     doAction : function(action, options){
8297         if(typeof action == 'string'){
8298             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8299         }
8300         if(this.fireEvent('beforeaction', this, action) !== false){
8301             this.beforeAction(action);
8302             action.run.defer(100, action);
8303         }
8304         return this;
8305     },
8306
8307     // private
8308     beforeAction : function(action){
8309         var o = action.options;
8310         
8311         if(this.loadMask){
8312             
8313             if(this.maskBody){
8314                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8315             } else {
8316                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8317             }
8318         }
8319         // not really supported yet.. ??
8320
8321         //if(this.waitMsgTarget === true){
8322         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8323         //}else if(this.waitMsgTarget){
8324         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8325         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8326         //}else {
8327         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8328        // }
8329
8330     },
8331
8332     // private
8333     afterAction : function(action, success){
8334         this.activeAction = null;
8335         var o = action.options;
8336
8337         if(this.loadMask){
8338             
8339             if(this.maskBody){
8340                 Roo.get(document.body).unmask();
8341             } else {
8342                 this.el.unmask();
8343             }
8344         }
8345         
8346         //if(this.waitMsgTarget === true){
8347 //            this.el.unmask();
8348         //}else if(this.waitMsgTarget){
8349         //    this.waitMsgTarget.unmask();
8350         //}else{
8351         //    Roo.MessageBox.updateProgress(1);
8352         //    Roo.MessageBox.hide();
8353        // }
8354         //
8355         if(success){
8356             if(o.reset){
8357                 this.reset();
8358             }
8359             Roo.callback(o.success, o.scope, [this, action]);
8360             this.fireEvent('actioncomplete', this, action);
8361
8362         }else{
8363
8364             // failure condition..
8365             // we have a scenario where updates need confirming.
8366             // eg. if a locking scenario exists..
8367             // we look for { errors : { needs_confirm : true }} in the response.
8368             if (
8369                 (typeof(action.result) != 'undefined')  &&
8370                 (typeof(action.result.errors) != 'undefined')  &&
8371                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8372            ){
8373                 var _t = this;
8374                 Roo.log("not supported yet");
8375                  /*
8376
8377                 Roo.MessageBox.confirm(
8378                     "Change requires confirmation",
8379                     action.result.errorMsg,
8380                     function(r) {
8381                         if (r != 'yes') {
8382                             return;
8383                         }
8384                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8385                     }
8386
8387                 );
8388                 */
8389
8390
8391                 return;
8392             }
8393
8394             Roo.callback(o.failure, o.scope, [this, action]);
8395             // show an error message if no failed handler is set..
8396             if (!this.hasListener('actionfailed')) {
8397                 Roo.log("need to add dialog support");
8398                 /*
8399                 Roo.MessageBox.alert("Error",
8400                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8401                         action.result.errorMsg :
8402                         "Saving Failed, please check your entries or try again"
8403                 );
8404                 */
8405             }
8406
8407             this.fireEvent('actionfailed', this, action);
8408         }
8409
8410     },
8411     /**
8412      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8413      * @param {String} id The value to search for
8414      * @return Field
8415      */
8416     findField : function(id){
8417         var items = this.getItems();
8418         var field = items.get(id);
8419         if(!field){
8420              items.each(function(f){
8421                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8422                     field = f;
8423                     return false;
8424                 }
8425                 return true;
8426             });
8427         }
8428         return field || null;
8429     },
8430      /**
8431      * Mark fields in this form invalid in bulk.
8432      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8433      * @return {BasicForm} this
8434      */
8435     markInvalid : function(errors){
8436         if(errors instanceof Array){
8437             for(var i = 0, len = errors.length; i < len; i++){
8438                 var fieldError = errors[i];
8439                 var f = this.findField(fieldError.id);
8440                 if(f){
8441                     f.markInvalid(fieldError.msg);
8442                 }
8443             }
8444         }else{
8445             var field, id;
8446             for(id in errors){
8447                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8448                     field.markInvalid(errors[id]);
8449                 }
8450             }
8451         }
8452         //Roo.each(this.childForms || [], function (f) {
8453         //    f.markInvalid(errors);
8454         //});
8455
8456         return this;
8457     },
8458
8459     /**
8460      * Set values for fields in this form in bulk.
8461      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8462      * @return {BasicForm} this
8463      */
8464     setValues : function(values){
8465         if(values instanceof Array){ // array of objects
8466             for(var i = 0, len = values.length; i < len; i++){
8467                 var v = values[i];
8468                 var f = this.findField(v.id);
8469                 if(f){
8470                     f.setValue(v.value);
8471                     if(this.trackResetOnLoad){
8472                         f.originalValue = f.getValue();
8473                     }
8474                 }
8475             }
8476         }else{ // object hash
8477             var field, id;
8478             for(id in values){
8479                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8480
8481                     if (field.setFromData &&
8482                         field.valueField &&
8483                         field.displayField &&
8484                         // combos' with local stores can
8485                         // be queried via setValue()
8486                         // to set their value..
8487                         (field.store && !field.store.isLocal)
8488                         ) {
8489                         // it's a combo
8490                         var sd = { };
8491                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8492                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8493                         field.setFromData(sd);
8494
8495                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8496                         
8497                         field.setFromData(values);
8498                         
8499                     } else {
8500                         field.setValue(values[id]);
8501                     }
8502
8503
8504                     if(this.trackResetOnLoad){
8505                         field.originalValue = field.getValue();
8506                     }
8507                 }
8508             }
8509         }
8510
8511         //Roo.each(this.childForms || [], function (f) {
8512         //    f.setValues(values);
8513         //});
8514
8515         return this;
8516     },
8517
8518     /**
8519      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8520      * they are returned as an array.
8521      * @param {Boolean} asString
8522      * @return {Object}
8523      */
8524     getValues : function(asString){
8525         //if (this.childForms) {
8526             // copy values from the child forms
8527         //    Roo.each(this.childForms, function (f) {
8528         //        this.setValues(f.getValues());
8529         //    }, this);
8530         //}
8531
8532
8533
8534         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8535         if(asString === true){
8536             return fs;
8537         }
8538         return Roo.urlDecode(fs);
8539     },
8540
8541     /**
8542      * Returns the fields in this form as an object with key/value pairs.
8543      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8544      * @return {Object}
8545      */
8546     getFieldValues : function(with_hidden)
8547     {
8548         var items = this.getItems();
8549         var ret = {};
8550         items.each(function(f){
8551             
8552             if (!f.getName()) {
8553                 return;
8554             }
8555             
8556             var v = f.getValue();
8557             
8558             if (f.inputType =='radio') {
8559                 if (typeof(ret[f.getName()]) == 'undefined') {
8560                     ret[f.getName()] = ''; // empty..
8561                 }
8562
8563                 if (!f.el.dom.checked) {
8564                     return;
8565
8566                 }
8567                 v = f.el.dom.value;
8568
8569             }
8570             
8571             if(f.xtype == 'MoneyField'){
8572                 ret[f.currencyName] = f.getCurrency();
8573             }
8574
8575             // not sure if this supported any more..
8576             if ((typeof(v) == 'object') && f.getRawValue) {
8577                 v = f.getRawValue() ; // dates..
8578             }
8579             // combo boxes where name != hiddenName...
8580             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8581                 ret[f.name] = f.getRawValue();
8582             }
8583             ret[f.getName()] = v;
8584         });
8585
8586         return ret;
8587     },
8588
8589     /**
8590      * Clears all invalid messages in this form.
8591      * @return {BasicForm} this
8592      */
8593     clearInvalid : function(){
8594         var items = this.getItems();
8595
8596         items.each(function(f){
8597            f.clearInvalid();
8598         });
8599
8600         return this;
8601     },
8602
8603     /**
8604      * Resets this form.
8605      * @return {BasicForm} this
8606      */
8607     reset : function(){
8608         var items = this.getItems();
8609         items.each(function(f){
8610             f.reset();
8611         });
8612
8613         Roo.each(this.childForms || [], function (f) {
8614             f.reset();
8615         });
8616
8617
8618         return this;
8619     },
8620     
8621     getItems : function()
8622     {
8623         var r=new Roo.util.MixedCollection(false, function(o){
8624             return o.id || (o.id = Roo.id());
8625         });
8626         var iter = function(el) {
8627             if (el.inputEl) {
8628                 r.add(el);
8629             }
8630             if (!el.items) {
8631                 return;
8632             }
8633             Roo.each(el.items,function(e) {
8634                 iter(e);
8635             });
8636         };
8637
8638         iter(this);
8639         return r;
8640     },
8641     
8642     hideFields : function(items)
8643     {
8644         Roo.each(items, function(i){
8645             
8646             var f = this.findField(i);
8647             
8648             if(!f){
8649                 return;
8650             }
8651             
8652             f.hide();
8653             
8654         }, this);
8655     },
8656     
8657     showFields : function(items)
8658     {
8659         Roo.each(items, function(i){
8660             
8661             var f = this.findField(i);
8662             
8663             if(!f){
8664                 return;
8665             }
8666             
8667             f.show();
8668             
8669         }, this);
8670     }
8671
8672 });
8673
8674 Roo.apply(Roo.bootstrap.Form, {
8675     
8676     popover : {
8677         
8678         padding : 5,
8679         
8680         isApplied : false,
8681         
8682         isMasked : false,
8683         
8684         form : false,
8685         
8686         target : false,
8687         
8688         toolTip : false,
8689         
8690         intervalID : false,
8691         
8692         maskEl : false,
8693         
8694         apply : function()
8695         {
8696             if(this.isApplied){
8697                 return;
8698             }
8699             
8700             this.maskEl = {
8701                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8702                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8703                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8704                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8705             };
8706             
8707             this.maskEl.top.enableDisplayMode("block");
8708             this.maskEl.left.enableDisplayMode("block");
8709             this.maskEl.bottom.enableDisplayMode("block");
8710             this.maskEl.right.enableDisplayMode("block");
8711             
8712             this.toolTip = new Roo.bootstrap.Tooltip({
8713                 cls : 'roo-form-error-popover',
8714                 alignment : {
8715                     'left' : ['r-l', [-2,0], 'right'],
8716                     'right' : ['l-r', [2,0], 'left'],
8717                     'bottom' : ['tl-bl', [0,2], 'top'],
8718                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8719                 }
8720             });
8721             
8722             this.toolTip.render(Roo.get(document.body));
8723
8724             this.toolTip.el.enableDisplayMode("block");
8725             
8726             Roo.get(document.body).on('click', function(){
8727                 this.unmask();
8728             }, this);
8729             
8730             Roo.get(document.body).on('touchstart', function(){
8731                 this.unmask();
8732             }, this);
8733             
8734             this.isApplied = true
8735         },
8736         
8737         mask : function(form, target)
8738         {
8739             this.form = form;
8740             
8741             this.target = target;
8742             
8743             if(!this.form.errorMask || !target.el){
8744                 return;
8745             }
8746             
8747             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8748             
8749             Roo.log(scrollable);
8750             
8751             var ot = this.target.el.calcOffsetsTo(scrollable);
8752             
8753             var scrollTo = ot[1] - this.form.maskOffset;
8754             
8755             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8756             
8757             scrollable.scrollTo('top', scrollTo);
8758             
8759             var box = this.target.el.getBox();
8760             Roo.log(box);
8761             var zIndex = Roo.bootstrap.Modal.zIndex++;
8762
8763             
8764             this.maskEl.top.setStyle('position', 'absolute');
8765             this.maskEl.top.setStyle('z-index', zIndex);
8766             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8767             this.maskEl.top.setLeft(0);
8768             this.maskEl.top.setTop(0);
8769             this.maskEl.top.show();
8770             
8771             this.maskEl.left.setStyle('position', 'absolute');
8772             this.maskEl.left.setStyle('z-index', zIndex);
8773             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8774             this.maskEl.left.setLeft(0);
8775             this.maskEl.left.setTop(box.y - this.padding);
8776             this.maskEl.left.show();
8777
8778             this.maskEl.bottom.setStyle('position', 'absolute');
8779             this.maskEl.bottom.setStyle('z-index', zIndex);
8780             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8781             this.maskEl.bottom.setLeft(0);
8782             this.maskEl.bottom.setTop(box.bottom + this.padding);
8783             this.maskEl.bottom.show();
8784
8785             this.maskEl.right.setStyle('position', 'absolute');
8786             this.maskEl.right.setStyle('z-index', zIndex);
8787             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8788             this.maskEl.right.setLeft(box.right + this.padding);
8789             this.maskEl.right.setTop(box.y - this.padding);
8790             this.maskEl.right.show();
8791
8792             this.toolTip.bindEl = this.target.el;
8793
8794             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8795
8796             var tip = this.target.blankText;
8797
8798             if(this.target.getValue() !== '' ) {
8799                 
8800                 if (this.target.invalidText.length) {
8801                     tip = this.target.invalidText;
8802                 } else if (this.target.regexText.length){
8803                     tip = this.target.regexText;
8804                 }
8805             }
8806
8807             this.toolTip.show(tip);
8808
8809             this.intervalID = window.setInterval(function() {
8810                 Roo.bootstrap.Form.popover.unmask();
8811             }, 10000);
8812
8813             window.onwheel = function(){ return false;};
8814             
8815             (function(){ this.isMasked = true; }).defer(500, this);
8816             
8817         },
8818         
8819         unmask : function()
8820         {
8821             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8822                 return;
8823             }
8824             
8825             this.maskEl.top.setStyle('position', 'absolute');
8826             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8827             this.maskEl.top.hide();
8828
8829             this.maskEl.left.setStyle('position', 'absolute');
8830             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8831             this.maskEl.left.hide();
8832
8833             this.maskEl.bottom.setStyle('position', 'absolute');
8834             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8835             this.maskEl.bottom.hide();
8836
8837             this.maskEl.right.setStyle('position', 'absolute');
8838             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8839             this.maskEl.right.hide();
8840             
8841             this.toolTip.hide();
8842             
8843             this.toolTip.el.hide();
8844             
8845             window.onwheel = function(){ return true;};
8846             
8847             if(this.intervalID){
8848                 window.clearInterval(this.intervalID);
8849                 this.intervalID = false;
8850             }
8851             
8852             this.isMasked = false;
8853             
8854         }
8855         
8856     }
8857     
8858 });
8859
8860 /*
8861  * Based on:
8862  * Ext JS Library 1.1.1
8863  * Copyright(c) 2006-2007, Ext JS, LLC.
8864  *
8865  * Originally Released Under LGPL - original licence link has changed is not relivant.
8866  *
8867  * Fork - LGPL
8868  * <script type="text/javascript">
8869  */
8870 /**
8871  * @class Roo.form.VTypes
8872  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8873  * @singleton
8874  */
8875 Roo.form.VTypes = function(){
8876     // closure these in so they are only created once.
8877     var alpha = /^[a-zA-Z_]+$/;
8878     var alphanum = /^[a-zA-Z0-9_]+$/;
8879     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8880     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8881
8882     // All these messages and functions are configurable
8883     return {
8884         /**
8885          * The function used to validate email addresses
8886          * @param {String} value The email address
8887          */
8888         'email' : function(v){
8889             return email.test(v);
8890         },
8891         /**
8892          * The error text to display when the email validation function returns false
8893          * @type String
8894          */
8895         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8896         /**
8897          * The keystroke filter mask to be applied on email input
8898          * @type RegExp
8899          */
8900         'emailMask' : /[a-z0-9_\.\-@]/i,
8901
8902         /**
8903          * The function used to validate URLs
8904          * @param {String} value The URL
8905          */
8906         'url' : function(v){
8907             return url.test(v);
8908         },
8909         /**
8910          * The error text to display when the url validation function returns false
8911          * @type String
8912          */
8913         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8914         
8915         /**
8916          * The function used to validate alpha values
8917          * @param {String} value The value
8918          */
8919         'alpha' : function(v){
8920             return alpha.test(v);
8921         },
8922         /**
8923          * The error text to display when the alpha validation function returns false
8924          * @type String
8925          */
8926         'alphaText' : 'This field should only contain letters and _',
8927         /**
8928          * The keystroke filter mask to be applied on alpha input
8929          * @type RegExp
8930          */
8931         'alphaMask' : /[a-z_]/i,
8932
8933         /**
8934          * The function used to validate alphanumeric values
8935          * @param {String} value The value
8936          */
8937         'alphanum' : function(v){
8938             return alphanum.test(v);
8939         },
8940         /**
8941          * The error text to display when the alphanumeric validation function returns false
8942          * @type String
8943          */
8944         'alphanumText' : 'This field should only contain letters, numbers and _',
8945         /**
8946          * The keystroke filter mask to be applied on alphanumeric input
8947          * @type RegExp
8948          */
8949         'alphanumMask' : /[a-z0-9_]/i
8950     };
8951 }();/*
8952  * - LGPL
8953  *
8954  * Input
8955  * 
8956  */
8957
8958 /**
8959  * @class Roo.bootstrap.Input
8960  * @extends Roo.bootstrap.Component
8961  * Bootstrap Input class
8962  * @cfg {Boolean} disabled is it disabled
8963  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8964  * @cfg {String} name name of the input
8965  * @cfg {string} fieldLabel - the label associated
8966  * @cfg {string} placeholder - placeholder to put in text.
8967  * @cfg {string}  before - input group add on before
8968  * @cfg {string} after - input group add on after
8969  * @cfg {string} size - (lg|sm) or leave empty..
8970  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8971  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8972  * @cfg {Number} md colspan out of 12 for computer-sized screens
8973  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8974  * @cfg {string} value default value of the input
8975  * @cfg {Number} labelWidth set the width of label 
8976  * @cfg {Number} labellg set the width of label (1-12)
8977  * @cfg {Number} labelmd set the width of label (1-12)
8978  * @cfg {Number} labelsm set the width of label (1-12)
8979  * @cfg {Number} labelxs set the width of label (1-12)
8980  * @cfg {String} labelAlign (top|left)
8981  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8982  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8983  * @cfg {String} indicatorpos (left|right) default left
8984  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8985  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8986
8987  * @cfg {String} align (left|center|right) Default left
8988  * @cfg {Boolean} forceFeedback (true|false) Default false
8989  * 
8990  * @constructor
8991  * Create a new Input
8992  * @param {Object} config The config object
8993  */
8994
8995 Roo.bootstrap.Input = function(config){
8996     
8997     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8998     
8999     this.addEvents({
9000         /**
9001          * @event focus
9002          * Fires when this field receives input focus.
9003          * @param {Roo.form.Field} this
9004          */
9005         focus : true,
9006         /**
9007          * @event blur
9008          * Fires when this field loses input focus.
9009          * @param {Roo.form.Field} this
9010          */
9011         blur : true,
9012         /**
9013          * @event specialkey
9014          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9015          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9016          * @param {Roo.form.Field} this
9017          * @param {Roo.EventObject} e The event object
9018          */
9019         specialkey : true,
9020         /**
9021          * @event change
9022          * Fires just before the field blurs if the field value has changed.
9023          * @param {Roo.form.Field} this
9024          * @param {Mixed} newValue The new value
9025          * @param {Mixed} oldValue The original value
9026          */
9027         change : true,
9028         /**
9029          * @event invalid
9030          * Fires after the field has been marked as invalid.
9031          * @param {Roo.form.Field} this
9032          * @param {String} msg The validation message
9033          */
9034         invalid : true,
9035         /**
9036          * @event valid
9037          * Fires after the field has been validated with no errors.
9038          * @param {Roo.form.Field} this
9039          */
9040         valid : true,
9041          /**
9042          * @event keyup
9043          * Fires after the key up
9044          * @param {Roo.form.Field} this
9045          * @param {Roo.EventObject}  e The event Object
9046          */
9047         keyup : true
9048     });
9049 };
9050
9051 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9052      /**
9053      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9054       automatic validation (defaults to "keyup").
9055      */
9056     validationEvent : "keyup",
9057      /**
9058      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9059      */
9060     validateOnBlur : true,
9061     /**
9062      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9063      */
9064     validationDelay : 250,
9065      /**
9066      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9067      */
9068     focusClass : "x-form-focus",  // not needed???
9069     
9070        
9071     /**
9072      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9073      */
9074     invalidClass : "has-warning",
9075     
9076     /**
9077      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9078      */
9079     validClass : "has-success",
9080     
9081     /**
9082      * @cfg {Boolean} hasFeedback (true|false) default true
9083      */
9084     hasFeedback : true,
9085     
9086     /**
9087      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9088      */
9089     invalidFeedbackClass : "glyphicon-warning-sign",
9090     
9091     /**
9092      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9093      */
9094     validFeedbackClass : "glyphicon-ok",
9095     
9096     /**
9097      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9098      */
9099     selectOnFocus : false,
9100     
9101      /**
9102      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9103      */
9104     maskRe : null,
9105        /**
9106      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9107      */
9108     vtype : null,
9109     
9110       /**
9111      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9112      */
9113     disableKeyFilter : false,
9114     
9115        /**
9116      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9117      */
9118     disabled : false,
9119      /**
9120      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9121      */
9122     allowBlank : true,
9123     /**
9124      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9125      */
9126     blankText : "Please complete this mandatory field",
9127     
9128      /**
9129      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9130      */
9131     minLength : 0,
9132     /**
9133      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9134      */
9135     maxLength : Number.MAX_VALUE,
9136     /**
9137      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9138      */
9139     minLengthText : "The minimum length for this field is {0}",
9140     /**
9141      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9142      */
9143     maxLengthText : "The maximum length for this field is {0}",
9144   
9145     
9146     /**
9147      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9148      * If available, this function will be called only after the basic validators all return true, and will be passed the
9149      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9150      */
9151     validator : null,
9152     /**
9153      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9154      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9155      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9156      */
9157     regex : null,
9158     /**
9159      * @cfg {String} regexText -- Depricated - use Invalid Text
9160      */
9161     regexText : "",
9162     
9163     /**
9164      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9165      */
9166     invalidText : "",
9167     
9168     
9169     
9170     autocomplete: false,
9171     
9172     
9173     fieldLabel : '',
9174     inputType : 'text',
9175     
9176     name : false,
9177     placeholder: false,
9178     before : false,
9179     after : false,
9180     size : false,
9181     hasFocus : false,
9182     preventMark: false,
9183     isFormField : true,
9184     value : '',
9185     labelWidth : 2,
9186     labelAlign : false,
9187     readOnly : false,
9188     align : false,
9189     formatedValue : false,
9190     forceFeedback : false,
9191     
9192     indicatorpos : 'left',
9193     
9194     labellg : 0,
9195     labelmd : 0,
9196     labelsm : 0,
9197     labelxs : 0,
9198     
9199     capture : '',
9200     accept : '',
9201     
9202     parentLabelAlign : function()
9203     {
9204         var parent = this;
9205         while (parent.parent()) {
9206             parent = parent.parent();
9207             if (typeof(parent.labelAlign) !='undefined') {
9208                 return parent.labelAlign;
9209             }
9210         }
9211         return 'left';
9212         
9213     },
9214     
9215     getAutoCreate : function()
9216     {
9217         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9218         
9219         var id = Roo.id();
9220         
9221         var cfg = {};
9222         
9223         if(this.inputType != 'hidden'){
9224             cfg.cls = 'form-group' //input-group
9225         }
9226         
9227         var input =  {
9228             tag: 'input',
9229             id : id,
9230             type : this.inputType,
9231             value : this.value,
9232             cls : 'form-control',
9233             placeholder : this.placeholder || '',
9234             autocomplete : this.autocomplete || 'new-password'
9235         };
9236         
9237         if(this.capture.length){
9238             input.capture = this.capture;
9239         }
9240         
9241         if(this.accept.length){
9242             input.accept = this.accept + "/*";
9243         }
9244         
9245         if(this.align){
9246             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9247         }
9248         
9249         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9250             input.maxLength = this.maxLength;
9251         }
9252         
9253         if (this.disabled) {
9254             input.disabled=true;
9255         }
9256         
9257         if (this.readOnly) {
9258             input.readonly=true;
9259         }
9260         
9261         if (this.name) {
9262             input.name = this.name;
9263         }
9264         
9265         if (this.size) {
9266             input.cls += ' input-' + this.size;
9267         }
9268         
9269         var settings=this;
9270         ['xs','sm','md','lg'].map(function(size){
9271             if (settings[size]) {
9272                 cfg.cls += ' col-' + size + '-' + settings[size];
9273             }
9274         });
9275         
9276         var inputblock = input;
9277         
9278         var feedback = {
9279             tag: 'span',
9280             cls: 'glyphicon form-control-feedback'
9281         };
9282             
9283         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9284             
9285             inputblock = {
9286                 cls : 'has-feedback',
9287                 cn :  [
9288                     input,
9289                     feedback
9290                 ] 
9291             };  
9292         }
9293         
9294         if (this.before || this.after) {
9295             
9296             inputblock = {
9297                 cls : 'input-group',
9298                 cn :  [] 
9299             };
9300             
9301             if (this.before && typeof(this.before) == 'string') {
9302                 
9303                 inputblock.cn.push({
9304                     tag :'span',
9305                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9306                     html : this.before
9307                 });
9308             }
9309             if (this.before && typeof(this.before) == 'object') {
9310                 this.before = Roo.factory(this.before);
9311                 
9312                 inputblock.cn.push({
9313                     tag :'span',
9314                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9315                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9316                 });
9317             }
9318             
9319             inputblock.cn.push(input);
9320             
9321             if (this.after && typeof(this.after) == 'string') {
9322                 inputblock.cn.push({
9323                     tag :'span',
9324                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9325                     html : this.after
9326                 });
9327             }
9328             if (this.after && typeof(this.after) == 'object') {
9329                 this.after = Roo.factory(this.after);
9330                 
9331                 inputblock.cn.push({
9332                     tag :'span',
9333                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9334                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9335                 });
9336             }
9337             
9338             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9339                 inputblock.cls += ' has-feedback';
9340                 inputblock.cn.push(feedback);
9341             }
9342         };
9343         var indicator = {
9344             tag : 'i',
9345             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9346             tooltip : 'This field is required'
9347         };
9348         if (Roo.bootstrap.version == 4) {
9349             indicator = {
9350                 tag : 'i',
9351                 style : 'display-none'
9352             };
9353         }
9354         if (align ==='left' && this.fieldLabel.length) {
9355             
9356             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9357             
9358             cfg.cn = [
9359                 indicator,
9360                 {
9361                     tag: 'label',
9362                     'for' :  id,
9363                     cls : 'control-label col-form-label',
9364                     html : this.fieldLabel
9365
9366                 },
9367                 {
9368                     cls : "", 
9369                     cn: [
9370                         inputblock
9371                     ]
9372                 }
9373             ];
9374             
9375             var labelCfg = cfg.cn[1];
9376             var contentCfg = cfg.cn[2];
9377             
9378             if(this.indicatorpos == 'right'){
9379                 cfg.cn = [
9380                     {
9381                         tag: 'label',
9382                         'for' :  id,
9383                         cls : 'control-label col-form-label',
9384                         cn : [
9385                             {
9386                                 tag : 'span',
9387                                 html : this.fieldLabel
9388                             },
9389                             indicator
9390                         ]
9391                     },
9392                     {
9393                         cls : "",
9394                         cn: [
9395                             inputblock
9396                         ]
9397                     }
9398
9399                 ];
9400                 
9401                 labelCfg = cfg.cn[0];
9402                 contentCfg = cfg.cn[1];
9403             
9404             }
9405             
9406             if(this.labelWidth > 12){
9407                 labelCfg.style = "width: " + this.labelWidth + 'px';
9408             }
9409             
9410             if(this.labelWidth < 13 && this.labelmd == 0){
9411                 this.labelmd = this.labelWidth;
9412             }
9413             
9414             if(this.labellg > 0){
9415                 labelCfg.cls += ' col-lg-' + this.labellg;
9416                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9417             }
9418             
9419             if(this.labelmd > 0){
9420                 labelCfg.cls += ' col-md-' + this.labelmd;
9421                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9422             }
9423             
9424             if(this.labelsm > 0){
9425                 labelCfg.cls += ' col-sm-' + this.labelsm;
9426                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9427             }
9428             
9429             if(this.labelxs > 0){
9430                 labelCfg.cls += ' col-xs-' + this.labelxs;
9431                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9432             }
9433             
9434             
9435         } else if ( this.fieldLabel.length) {
9436                 
9437             cfg.cn = [
9438                 {
9439                     tag : 'i',
9440                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9441                     tooltip : 'This field is required'
9442                 },
9443                 {
9444                     tag: 'label',
9445                    //cls : 'input-group-addon',
9446                     html : this.fieldLabel
9447
9448                 },
9449
9450                inputblock
9451
9452            ];
9453            
9454            if(this.indicatorpos == 'right'){
9455                 
9456                 cfg.cn = [
9457                     {
9458                         tag: 'label',
9459                        //cls : 'input-group-addon',
9460                         html : this.fieldLabel
9461
9462                     },
9463                     {
9464                         tag : 'i',
9465                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9466                         tooltip : 'This field is required'
9467                     },
9468
9469                    inputblock
9470
9471                ];
9472
9473             }
9474
9475         } else {
9476             
9477             cfg.cn = [
9478
9479                     inputblock
9480
9481             ];
9482                 
9483                 
9484         };
9485         
9486         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9487            cfg.cls += ' navbar-form';
9488         }
9489         
9490         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9491             // on BS4 we do this only if not form 
9492             cfg.cls += ' navbar-form';
9493             cfg.tag = 'li';
9494         }
9495         
9496         return cfg;
9497         
9498     },
9499     /**
9500      * return the real input element.
9501      */
9502     inputEl: function ()
9503     {
9504         return this.el.select('input.form-control',true).first();
9505     },
9506     
9507     tooltipEl : function()
9508     {
9509         return this.inputEl();
9510     },
9511     
9512     indicatorEl : function()
9513     {
9514         if (Roo.bootstrap.version == 4) {
9515             return false; // not enabled in v4 yet.
9516         }
9517         
9518         var indicator = this.el.select('i.roo-required-indicator',true).first();
9519         
9520         if(!indicator){
9521             return false;
9522         }
9523         
9524         return indicator;
9525         
9526     },
9527     
9528     setDisabled : function(v)
9529     {
9530         var i  = this.inputEl().dom;
9531         if (!v) {
9532             i.removeAttribute('disabled');
9533             return;
9534             
9535         }
9536         i.setAttribute('disabled','true');
9537     },
9538     initEvents : function()
9539     {
9540           
9541         this.inputEl().on("keydown" , this.fireKey,  this);
9542         this.inputEl().on("focus", this.onFocus,  this);
9543         this.inputEl().on("blur", this.onBlur,  this);
9544         
9545         this.inputEl().relayEvent('keyup', this);
9546         
9547         this.indicator = this.indicatorEl();
9548         
9549         if(this.indicator){
9550             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9551         }
9552  
9553         // reference to original value for reset
9554         this.originalValue = this.getValue();
9555         //Roo.form.TextField.superclass.initEvents.call(this);
9556         if(this.validationEvent == 'keyup'){
9557             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9558             this.inputEl().on('keyup', this.filterValidation, this);
9559         }
9560         else if(this.validationEvent !== false){
9561             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9562         }
9563         
9564         if(this.selectOnFocus){
9565             this.on("focus", this.preFocus, this);
9566             
9567         }
9568         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9569             this.inputEl().on("keypress", this.filterKeys, this);
9570         } else {
9571             this.inputEl().relayEvent('keypress', this);
9572         }
9573        /* if(this.grow){
9574             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9575             this.el.on("click", this.autoSize,  this);
9576         }
9577         */
9578         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9579             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9580         }
9581         
9582         if (typeof(this.before) == 'object') {
9583             this.before.render(this.el.select('.roo-input-before',true).first());
9584         }
9585         if (typeof(this.after) == 'object') {
9586             this.after.render(this.el.select('.roo-input-after',true).first());
9587         }
9588         
9589         this.inputEl().on('change', this.onChange, this);
9590         
9591     },
9592     filterValidation : function(e){
9593         if(!e.isNavKeyPress()){
9594             this.validationTask.delay(this.validationDelay);
9595         }
9596     },
9597      /**
9598      * Validates the field value
9599      * @return {Boolean} True if the value is valid, else false
9600      */
9601     validate : function(){
9602         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9603         if(this.disabled || this.validateValue(this.getRawValue())){
9604             this.markValid();
9605             return true;
9606         }
9607         
9608         this.markInvalid();
9609         return false;
9610     },
9611     
9612     
9613     /**
9614      * Validates a value according to the field's validation rules and marks the field as invalid
9615      * if the validation fails
9616      * @param {Mixed} value The value to validate
9617      * @return {Boolean} True if the value is valid, else false
9618      */
9619     validateValue : function(value)
9620     {
9621         if(this.getVisibilityEl().hasClass('hidden')){
9622             return true;
9623         }
9624         
9625         if(value.length < 1)  { // if it's blank
9626             if(this.allowBlank){
9627                 return true;
9628             }
9629             return false;
9630         }
9631         
9632         if(value.length < this.minLength){
9633             return false;
9634         }
9635         if(value.length > this.maxLength){
9636             return false;
9637         }
9638         if(this.vtype){
9639             var vt = Roo.form.VTypes;
9640             if(!vt[this.vtype](value, this)){
9641                 return false;
9642             }
9643         }
9644         if(typeof this.validator == "function"){
9645             var msg = this.validator(value);
9646             if(msg !== true){
9647                 return false;
9648             }
9649             if (typeof(msg) == 'string') {
9650                 this.invalidText = msg;
9651             }
9652         }
9653         
9654         if(this.regex && !this.regex.test(value)){
9655             return false;
9656         }
9657         
9658         return true;
9659     },
9660     
9661      // private
9662     fireKey : function(e){
9663         //Roo.log('field ' + e.getKey());
9664         if(e.isNavKeyPress()){
9665             this.fireEvent("specialkey", this, e);
9666         }
9667     },
9668     focus : function (selectText){
9669         if(this.rendered){
9670             this.inputEl().focus();
9671             if(selectText === true){
9672                 this.inputEl().dom.select();
9673             }
9674         }
9675         return this;
9676     } ,
9677     
9678     onFocus : function(){
9679         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9680            // this.el.addClass(this.focusClass);
9681         }
9682         if(!this.hasFocus){
9683             this.hasFocus = true;
9684             this.startValue = this.getValue();
9685             this.fireEvent("focus", this);
9686         }
9687     },
9688     
9689     beforeBlur : Roo.emptyFn,
9690
9691     
9692     // private
9693     onBlur : function(){
9694         this.beforeBlur();
9695         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9696             //this.el.removeClass(this.focusClass);
9697         }
9698         this.hasFocus = false;
9699         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9700             this.validate();
9701         }
9702         var v = this.getValue();
9703         if(String(v) !== String(this.startValue)){
9704             this.fireEvent('change', this, v, this.startValue);
9705         }
9706         this.fireEvent("blur", this);
9707     },
9708     
9709     onChange : function(e)
9710     {
9711         var v = this.getValue();
9712         if(String(v) !== String(this.startValue)){
9713             this.fireEvent('change', this, v, this.startValue);
9714         }
9715         
9716     },
9717     
9718     /**
9719      * Resets the current field value to the originally loaded value and clears any validation messages
9720      */
9721     reset : function(){
9722         this.setValue(this.originalValue);
9723         this.validate();
9724     },
9725      /**
9726      * Returns the name of the field
9727      * @return {Mixed} name The name field
9728      */
9729     getName: function(){
9730         return this.name;
9731     },
9732      /**
9733      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9734      * @return {Mixed} value The field value
9735      */
9736     getValue : function(){
9737         
9738         var v = this.inputEl().getValue();
9739         
9740         return v;
9741     },
9742     /**
9743      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9744      * @return {Mixed} value The field value
9745      */
9746     getRawValue : function(){
9747         var v = this.inputEl().getValue();
9748         
9749         return v;
9750     },
9751     
9752     /**
9753      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9754      * @param {Mixed} value The value to set
9755      */
9756     setRawValue : function(v){
9757         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9758     },
9759     
9760     selectText : function(start, end){
9761         var v = this.getRawValue();
9762         if(v.length > 0){
9763             start = start === undefined ? 0 : start;
9764             end = end === undefined ? v.length : end;
9765             var d = this.inputEl().dom;
9766             if(d.setSelectionRange){
9767                 d.setSelectionRange(start, end);
9768             }else if(d.createTextRange){
9769                 var range = d.createTextRange();
9770                 range.moveStart("character", start);
9771                 range.moveEnd("character", v.length-end);
9772                 range.select();
9773             }
9774         }
9775     },
9776     
9777     /**
9778      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9779      * @param {Mixed} value The value to set
9780      */
9781     setValue : function(v){
9782         this.value = v;
9783         if(this.rendered){
9784             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9785             this.validate();
9786         }
9787     },
9788     
9789     /*
9790     processValue : function(value){
9791         if(this.stripCharsRe){
9792             var newValue = value.replace(this.stripCharsRe, '');
9793             if(newValue !== value){
9794                 this.setRawValue(newValue);
9795                 return newValue;
9796             }
9797         }
9798         return value;
9799     },
9800   */
9801     preFocus : function(){
9802         
9803         if(this.selectOnFocus){
9804             this.inputEl().dom.select();
9805         }
9806     },
9807     filterKeys : function(e){
9808         var k = e.getKey();
9809         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9810             return;
9811         }
9812         var c = e.getCharCode(), cc = String.fromCharCode(c);
9813         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9814             return;
9815         }
9816         if(!this.maskRe.test(cc)){
9817             e.stopEvent();
9818         }
9819     },
9820      /**
9821      * Clear any invalid styles/messages for this field
9822      */
9823     clearInvalid : function(){
9824         
9825         if(!this.el || this.preventMark){ // not rendered
9826             return;
9827         }
9828         
9829         
9830         this.el.removeClass([this.invalidClass, 'is-invalid']);
9831         
9832         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9833             
9834             var feedback = this.el.select('.form-control-feedback', true).first();
9835             
9836             if(feedback){
9837                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9838             }
9839             
9840         }
9841         
9842         if(this.indicator){
9843             this.indicator.removeClass('visible');
9844             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9845         }
9846         
9847         this.fireEvent('valid', this);
9848     },
9849     
9850      /**
9851      * Mark this field as valid
9852      */
9853     markValid : function()
9854     {
9855         if(!this.el  || this.preventMark){ // not rendered...
9856             return;
9857         }
9858         
9859         this.el.removeClass([this.invalidClass, this.validClass]);
9860         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9861
9862         var feedback = this.el.select('.form-control-feedback', true).first();
9863             
9864         if(feedback){
9865             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9866         }
9867         
9868         if(this.indicator){
9869             this.indicator.removeClass('visible');
9870             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9871         }
9872         
9873         if(this.disabled){
9874             return;
9875         }
9876         
9877         if(this.allowBlank && !this.getRawValue().length){
9878             return;
9879         }
9880         if (Roo.bootstrap.version == 3) {
9881             this.el.addClass(this.validClass);
9882         } else {
9883             this.inputEl().addClass('is-valid');
9884         }
9885
9886         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9887             
9888             var feedback = this.el.select('.form-control-feedback', true).first();
9889             
9890             if(feedback){
9891                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9892                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9893             }
9894             
9895         }
9896         
9897         this.fireEvent('valid', this);
9898     },
9899     
9900      /**
9901      * Mark this field as invalid
9902      * @param {String} msg The validation message
9903      */
9904     markInvalid : function(msg)
9905     {
9906         if(!this.el  || this.preventMark){ // not rendered
9907             return;
9908         }
9909         
9910         this.el.removeClass([this.invalidClass, this.validClass]);
9911         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9912         
9913         var feedback = this.el.select('.form-control-feedback', true).first();
9914             
9915         if(feedback){
9916             this.el.select('.form-control-feedback', true).first().removeClass(
9917                     [this.invalidFeedbackClass, this.validFeedbackClass]);
9918         }
9919
9920         if(this.disabled){
9921             return;
9922         }
9923         
9924         if(this.allowBlank && !this.getRawValue().length){
9925             return;
9926         }
9927         
9928         if(this.indicator){
9929             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9930             this.indicator.addClass('visible');
9931         }
9932         if (Roo.bootstrap.version == 3) {
9933             this.el.addClass(this.invalidClass);
9934         } else {
9935             this.inputEl().addClass('is-invalid');
9936         }
9937         
9938         
9939         
9940         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9941             
9942             var feedback = this.el.select('.form-control-feedback', true).first();
9943             
9944             if(feedback){
9945                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9946                 
9947                 if(this.getValue().length || this.forceFeedback){
9948                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9949                 }
9950                 
9951             }
9952             
9953         }
9954         
9955         this.fireEvent('invalid', this, msg);
9956     },
9957     // private
9958     SafariOnKeyDown : function(event)
9959     {
9960         // this is a workaround for a password hang bug on chrome/ webkit.
9961         if (this.inputEl().dom.type != 'password') {
9962             return;
9963         }
9964         
9965         var isSelectAll = false;
9966         
9967         if(this.inputEl().dom.selectionEnd > 0){
9968             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9969         }
9970         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9971             event.preventDefault();
9972             this.setValue('');
9973             return;
9974         }
9975         
9976         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9977             
9978             event.preventDefault();
9979             // this is very hacky as keydown always get's upper case.
9980             //
9981             var cc = String.fromCharCode(event.getCharCode());
9982             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9983             
9984         }
9985     },
9986     adjustWidth : function(tag, w){
9987         tag = tag.toLowerCase();
9988         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9989             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9990                 if(tag == 'input'){
9991                     return w + 2;
9992                 }
9993                 if(tag == 'textarea'){
9994                     return w-2;
9995                 }
9996             }else if(Roo.isOpera){
9997                 if(tag == 'input'){
9998                     return w + 2;
9999                 }
10000                 if(tag == 'textarea'){
10001                     return w-2;
10002                 }
10003             }
10004         }
10005         return w;
10006     },
10007     
10008     setFieldLabel : function(v)
10009     {
10010         if(!this.rendered){
10011             return;
10012         }
10013         
10014         if(this.indicatorEl()){
10015             var ar = this.el.select('label > span',true);
10016             
10017             if (ar.elements.length) {
10018                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10019                 this.fieldLabel = v;
10020                 return;
10021             }
10022             
10023             var br = this.el.select('label',true);
10024             
10025             if(br.elements.length) {
10026                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10027                 this.fieldLabel = v;
10028                 return;
10029             }
10030             
10031             Roo.log('Cannot Found any of label > span || label in input');
10032             return;
10033         }
10034         
10035         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10036         this.fieldLabel = v;
10037         
10038         
10039     }
10040 });
10041
10042  
10043 /*
10044  * - LGPL
10045  *
10046  * Input
10047  * 
10048  */
10049
10050 /**
10051  * @class Roo.bootstrap.TextArea
10052  * @extends Roo.bootstrap.Input
10053  * Bootstrap TextArea class
10054  * @cfg {Number} cols Specifies the visible width of a text area
10055  * @cfg {Number} rows Specifies the visible number of lines in a text area
10056  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10057  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10058  * @cfg {string} html text
10059  * 
10060  * @constructor
10061  * Create a new TextArea
10062  * @param {Object} config The config object
10063  */
10064
10065 Roo.bootstrap.TextArea = function(config){
10066     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10067    
10068 };
10069
10070 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10071      
10072     cols : false,
10073     rows : 5,
10074     readOnly : false,
10075     warp : 'soft',
10076     resize : false,
10077     value: false,
10078     html: false,
10079     
10080     getAutoCreate : function(){
10081         
10082         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10083         
10084         var id = Roo.id();
10085         
10086         var cfg = {};
10087         
10088         if(this.inputType != 'hidden'){
10089             cfg.cls = 'form-group' //input-group
10090         }
10091         
10092         var input =  {
10093             tag: 'textarea',
10094             id : id,
10095             warp : this.warp,
10096             rows : this.rows,
10097             value : this.value || '',
10098             html: this.html || '',
10099             cls : 'form-control',
10100             placeholder : this.placeholder || '' 
10101             
10102         };
10103         
10104         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10105             input.maxLength = this.maxLength;
10106         }
10107         
10108         if(this.resize){
10109             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10110         }
10111         
10112         if(this.cols){
10113             input.cols = this.cols;
10114         }
10115         
10116         if (this.readOnly) {
10117             input.readonly = true;
10118         }
10119         
10120         if (this.name) {
10121             input.name = this.name;
10122         }
10123         
10124         if (this.size) {
10125             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10126         }
10127         
10128         var settings=this;
10129         ['xs','sm','md','lg'].map(function(size){
10130             if (settings[size]) {
10131                 cfg.cls += ' col-' + size + '-' + settings[size];
10132             }
10133         });
10134         
10135         var inputblock = input;
10136         
10137         if(this.hasFeedback && !this.allowBlank){
10138             
10139             var feedback = {
10140                 tag: 'span',
10141                 cls: 'glyphicon form-control-feedback'
10142             };
10143
10144             inputblock = {
10145                 cls : 'has-feedback',
10146                 cn :  [
10147                     input,
10148                     feedback
10149                 ] 
10150             };  
10151         }
10152         
10153         
10154         if (this.before || this.after) {
10155             
10156             inputblock = {
10157                 cls : 'input-group',
10158                 cn :  [] 
10159             };
10160             if (this.before) {
10161                 inputblock.cn.push({
10162                     tag :'span',
10163                     cls : 'input-group-addon',
10164                     html : this.before
10165                 });
10166             }
10167             
10168             inputblock.cn.push(input);
10169             
10170             if(this.hasFeedback && !this.allowBlank){
10171                 inputblock.cls += ' has-feedback';
10172                 inputblock.cn.push(feedback);
10173             }
10174             
10175             if (this.after) {
10176                 inputblock.cn.push({
10177                     tag :'span',
10178                     cls : 'input-group-addon',
10179                     html : this.after
10180                 });
10181             }
10182             
10183         }
10184         
10185         if (align ==='left' && this.fieldLabel.length) {
10186             cfg.cn = [
10187                 {
10188                     tag: 'label',
10189                     'for' :  id,
10190                     cls : 'control-label',
10191                     html : this.fieldLabel
10192                 },
10193                 {
10194                     cls : "",
10195                     cn: [
10196                         inputblock
10197                     ]
10198                 }
10199
10200             ];
10201             
10202             if(this.labelWidth > 12){
10203                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10204             }
10205
10206             if(this.labelWidth < 13 && this.labelmd == 0){
10207                 this.labelmd = this.labelWidth;
10208             }
10209
10210             if(this.labellg > 0){
10211                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10212                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10213             }
10214
10215             if(this.labelmd > 0){
10216                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10217                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10218             }
10219
10220             if(this.labelsm > 0){
10221                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10222                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10223             }
10224
10225             if(this.labelxs > 0){
10226                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10227                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10228             }
10229             
10230         } else if ( this.fieldLabel.length) {
10231             cfg.cn = [
10232
10233                {
10234                    tag: 'label',
10235                    //cls : 'input-group-addon',
10236                    html : this.fieldLabel
10237
10238                },
10239
10240                inputblock
10241
10242            ];
10243
10244         } else {
10245
10246             cfg.cn = [
10247
10248                 inputblock
10249
10250             ];
10251                 
10252         }
10253         
10254         if (this.disabled) {
10255             input.disabled=true;
10256         }
10257         
10258         return cfg;
10259         
10260     },
10261     /**
10262      * return the real textarea element.
10263      */
10264     inputEl: function ()
10265     {
10266         return this.el.select('textarea.form-control',true).first();
10267     },
10268     
10269     /**
10270      * Clear any invalid styles/messages for this field
10271      */
10272     clearInvalid : function()
10273     {
10274         
10275         if(!this.el || this.preventMark){ // not rendered
10276             return;
10277         }
10278         
10279         var label = this.el.select('label', true).first();
10280         var icon = this.el.select('i.fa-star', true).first();
10281         
10282         if(label && icon){
10283             icon.remove();
10284         }
10285         this.el.removeClass( this.validClass);
10286         this.inputEl().removeClass('is-invalid');
10287          
10288         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10289             
10290             var feedback = this.el.select('.form-control-feedback', true).first();
10291             
10292             if(feedback){
10293                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10294             }
10295             
10296         }
10297         
10298         this.fireEvent('valid', this);
10299     },
10300     
10301      /**
10302      * Mark this field as valid
10303      */
10304     markValid : function()
10305     {
10306         if(!this.el  || this.preventMark){ // not rendered
10307             return;
10308         }
10309         
10310         this.el.removeClass([this.invalidClass, this.validClass]);
10311         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10312         
10313         var feedback = this.el.select('.form-control-feedback', true).first();
10314             
10315         if(feedback){
10316             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10317         }
10318
10319         if(this.disabled || this.allowBlank){
10320             return;
10321         }
10322         
10323         var label = this.el.select('label', true).first();
10324         var icon = this.el.select('i.fa-star', true).first();
10325         
10326         if(label && icon){
10327             icon.remove();
10328         }
10329         if (Roo.bootstrap.version == 3) {
10330             this.el.addClass(this.validClass);
10331         } else {
10332             this.inputEl().addClass('is-valid');
10333         }
10334         
10335         
10336         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10337             
10338             var feedback = this.el.select('.form-control-feedback', true).first();
10339             
10340             if(feedback){
10341                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10342                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10343             }
10344             
10345         }
10346         
10347         this.fireEvent('valid', this);
10348     },
10349     
10350      /**
10351      * Mark this field as invalid
10352      * @param {String} msg The validation message
10353      */
10354     markInvalid : function(msg)
10355     {
10356         if(!this.el  || this.preventMark){ // not rendered
10357             return;
10358         }
10359         
10360         this.el.removeClass([this.invalidClass, this.validClass]);
10361         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10362         
10363         var feedback = this.el.select('.form-control-feedback', true).first();
10364             
10365         if(feedback){
10366             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10367         }
10368
10369         if(this.disabled || this.allowBlank){
10370             return;
10371         }
10372         
10373         var label = this.el.select('label', true).first();
10374         var icon = this.el.select('i.fa-star', true).first();
10375         
10376         if(!this.getValue().length && label && !icon){
10377             this.el.createChild({
10378                 tag : 'i',
10379                 cls : 'text-danger fa fa-lg fa-star',
10380                 tooltip : 'This field is required',
10381                 style : 'margin-right:5px;'
10382             }, label, true);
10383         }
10384         
10385         if (Roo.bootstrap.version == 3) {
10386             this.el.addClass(this.invalidClass);
10387         } else {
10388             this.inputEl().addClass('is-invalid');
10389         }
10390         
10391         // fixme ... this may be depricated need to test..
10392         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10393             
10394             var feedback = this.el.select('.form-control-feedback', true).first();
10395             
10396             if(feedback){
10397                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10398                 
10399                 if(this.getValue().length || this.forceFeedback){
10400                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10401                 }
10402                 
10403             }
10404             
10405         }
10406         
10407         this.fireEvent('invalid', this, msg);
10408     }
10409 });
10410
10411  
10412 /*
10413  * - LGPL
10414  *
10415  * trigger field - base class for combo..
10416  * 
10417  */
10418  
10419 /**
10420  * @class Roo.bootstrap.TriggerField
10421  * @extends Roo.bootstrap.Input
10422  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10423  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10424  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10425  * for which you can provide a custom implementation.  For example:
10426  * <pre><code>
10427 var trigger = new Roo.bootstrap.TriggerField();
10428 trigger.onTriggerClick = myTriggerFn;
10429 trigger.applyTo('my-field');
10430 </code></pre>
10431  *
10432  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10433  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10434  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10435  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10436  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10437
10438  * @constructor
10439  * Create a new TriggerField.
10440  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10441  * to the base TextField)
10442  */
10443 Roo.bootstrap.TriggerField = function(config){
10444     this.mimicing = false;
10445     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10446 };
10447
10448 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10449     /**
10450      * @cfg {String} triggerClass A CSS class to apply to the trigger
10451      */
10452      /**
10453      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10454      */
10455     hideTrigger:false,
10456
10457     /**
10458      * @cfg {Boolean} removable (true|false) special filter default false
10459      */
10460     removable : false,
10461     
10462     /** @cfg {Boolean} grow @hide */
10463     /** @cfg {Number} growMin @hide */
10464     /** @cfg {Number} growMax @hide */
10465
10466     /**
10467      * @hide 
10468      * @method
10469      */
10470     autoSize: Roo.emptyFn,
10471     // private
10472     monitorTab : true,
10473     // private
10474     deferHeight : true,
10475
10476     
10477     actionMode : 'wrap',
10478     
10479     caret : false,
10480     
10481     
10482     getAutoCreate : function(){
10483        
10484         var align = this.labelAlign || this.parentLabelAlign();
10485         
10486         var id = Roo.id();
10487         
10488         var cfg = {
10489             cls: 'form-group' //input-group
10490         };
10491         
10492         
10493         var input =  {
10494             tag: 'input',
10495             id : id,
10496             type : this.inputType,
10497             cls : 'form-control',
10498             autocomplete: 'new-password',
10499             placeholder : this.placeholder || '' 
10500             
10501         };
10502         if (this.name) {
10503             input.name = this.name;
10504         }
10505         if (this.size) {
10506             input.cls += ' input-' + this.size;
10507         }
10508         
10509         if (this.disabled) {
10510             input.disabled=true;
10511         }
10512         
10513         var inputblock = input;
10514         
10515         if(this.hasFeedback && !this.allowBlank){
10516             
10517             var feedback = {
10518                 tag: 'span',
10519                 cls: 'glyphicon form-control-feedback'
10520             };
10521             
10522             if(this.removable && !this.editable && !this.tickable){
10523                 inputblock = {
10524                     cls : 'has-feedback',
10525                     cn :  [
10526                         inputblock,
10527                         {
10528                             tag: 'button',
10529                             html : 'x',
10530                             cls : 'roo-combo-removable-btn close'
10531                         },
10532                         feedback
10533                     ] 
10534                 };
10535             } else {
10536                 inputblock = {
10537                     cls : 'has-feedback',
10538                     cn :  [
10539                         inputblock,
10540                         feedback
10541                     ] 
10542                 };
10543             }
10544
10545         } else {
10546             if(this.removable && !this.editable && !this.tickable){
10547                 inputblock = {
10548                     cls : 'roo-removable',
10549                     cn :  [
10550                         inputblock,
10551                         {
10552                             tag: 'button',
10553                             html : 'x',
10554                             cls : 'roo-combo-removable-btn close'
10555                         }
10556                     ] 
10557                 };
10558             }
10559         }
10560         
10561         if (this.before || this.after) {
10562             
10563             inputblock = {
10564                 cls : 'input-group',
10565                 cn :  [] 
10566             };
10567             if (this.before) {
10568                 inputblock.cn.push({
10569                     tag :'span',
10570                     cls : 'input-group-addon input-group-prepend input-group-text',
10571                     html : this.before
10572                 });
10573             }
10574             
10575             inputblock.cn.push(input);
10576             
10577             if(this.hasFeedback && !this.allowBlank){
10578                 inputblock.cls += ' has-feedback';
10579                 inputblock.cn.push(feedback);
10580             }
10581             
10582             if (this.after) {
10583                 inputblock.cn.push({
10584                     tag :'span',
10585                     cls : 'input-group-addon input-group-append input-group-text',
10586                     html : this.after
10587                 });
10588             }
10589             
10590         };
10591         
10592       
10593         
10594         var ibwrap = inputblock;
10595         
10596         if(this.multiple){
10597             ibwrap = {
10598                 tag: 'ul',
10599                 cls: 'roo-select2-choices',
10600                 cn:[
10601                     {
10602                         tag: 'li',
10603                         cls: 'roo-select2-search-field',
10604                         cn: [
10605
10606                             inputblock
10607                         ]
10608                     }
10609                 ]
10610             };
10611                 
10612         }
10613         
10614         var combobox = {
10615             cls: 'roo-select2-container input-group',
10616             cn: [
10617                  {
10618                     tag: 'input',
10619                     type : 'hidden',
10620                     cls: 'form-hidden-field'
10621                 },
10622                 ibwrap
10623             ]
10624         };
10625         
10626         if(!this.multiple && this.showToggleBtn){
10627             
10628             var caret = {
10629                         tag: 'span',
10630                         cls: 'caret'
10631              };
10632             if (this.caret != false) {
10633                 caret = {
10634                      tag: 'i',
10635                      cls: 'fa fa-' + this.caret
10636                 };
10637                 
10638             }
10639             
10640             combobox.cn.push({
10641                 tag :'span',
10642                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10643                 cn : [
10644                     caret,
10645                     {
10646                         tag: 'span',
10647                         cls: 'combobox-clear',
10648                         cn  : [
10649                             {
10650                                 tag : 'i',
10651                                 cls: 'icon-remove'
10652                             }
10653                         ]
10654                     }
10655                 ]
10656
10657             })
10658         }
10659         
10660         if(this.multiple){
10661             combobox.cls += ' roo-select2-container-multi';
10662         }
10663          var indicator = {
10664             tag : 'i',
10665             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10666             tooltip : 'This field is required'
10667         };
10668         if (Roo.bootstrap.version == 4) {
10669             indicator = {
10670                 tag : 'i',
10671                 style : 'display:none'
10672             };
10673         }
10674         
10675         
10676         if (align ==='left' && this.fieldLabel.length) {
10677             
10678             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10679
10680             cfg.cn = [
10681                 indicator,
10682                 {
10683                     tag: 'label',
10684                     'for' :  id,
10685                     cls : 'control-label',
10686                     html : this.fieldLabel
10687
10688                 },
10689                 {
10690                     cls : "", 
10691                     cn: [
10692                         combobox
10693                     ]
10694                 }
10695
10696             ];
10697             
10698             var labelCfg = cfg.cn[1];
10699             var contentCfg = cfg.cn[2];
10700             
10701             if(this.indicatorpos == 'right'){
10702                 cfg.cn = [
10703                     {
10704                         tag: 'label',
10705                         'for' :  id,
10706                         cls : 'control-label',
10707                         cn : [
10708                             {
10709                                 tag : 'span',
10710                                 html : this.fieldLabel
10711                             },
10712                             indicator
10713                         ]
10714                     },
10715                     {
10716                         cls : "", 
10717                         cn: [
10718                             combobox
10719                         ]
10720                     }
10721
10722                 ];
10723                 
10724                 labelCfg = cfg.cn[0];
10725                 contentCfg = cfg.cn[1];
10726             }
10727             
10728             if(this.labelWidth > 12){
10729                 labelCfg.style = "width: " + this.labelWidth + 'px';
10730             }
10731             
10732             if(this.labelWidth < 13 && this.labelmd == 0){
10733                 this.labelmd = this.labelWidth;
10734             }
10735             
10736             if(this.labellg > 0){
10737                 labelCfg.cls += ' col-lg-' + this.labellg;
10738                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10739             }
10740             
10741             if(this.labelmd > 0){
10742                 labelCfg.cls += ' col-md-' + this.labelmd;
10743                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10744             }
10745             
10746             if(this.labelsm > 0){
10747                 labelCfg.cls += ' col-sm-' + this.labelsm;
10748                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10749             }
10750             
10751             if(this.labelxs > 0){
10752                 labelCfg.cls += ' col-xs-' + this.labelxs;
10753                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10754             }
10755             
10756         } else if ( this.fieldLabel.length) {
10757 //                Roo.log(" label");
10758             cfg.cn = [
10759                 indicator,
10760                {
10761                    tag: 'label',
10762                    //cls : 'input-group-addon',
10763                    html : this.fieldLabel
10764
10765                },
10766
10767                combobox
10768
10769             ];
10770             
10771             if(this.indicatorpos == 'right'){
10772                 
10773                 cfg.cn = [
10774                     {
10775                        tag: 'label',
10776                        cn : [
10777                            {
10778                                tag : 'span',
10779                                html : this.fieldLabel
10780                            },
10781                            indicator
10782                        ]
10783
10784                     },
10785                     combobox
10786
10787                 ];
10788
10789             }
10790
10791         } else {
10792             
10793 //                Roo.log(" no label && no align");
10794                 cfg = combobox
10795                      
10796                 
10797         }
10798         
10799         var settings=this;
10800         ['xs','sm','md','lg'].map(function(size){
10801             if (settings[size]) {
10802                 cfg.cls += ' col-' + size + '-' + settings[size];
10803             }
10804         });
10805         
10806         return cfg;
10807         
10808     },
10809     
10810     
10811     
10812     // private
10813     onResize : function(w, h){
10814 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10815 //        if(typeof w == 'number'){
10816 //            var x = w - this.trigger.getWidth();
10817 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10818 //            this.trigger.setStyle('left', x+'px');
10819 //        }
10820     },
10821
10822     // private
10823     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10824
10825     // private
10826     getResizeEl : function(){
10827         return this.inputEl();
10828     },
10829
10830     // private
10831     getPositionEl : function(){
10832         return this.inputEl();
10833     },
10834
10835     // private
10836     alignErrorIcon : function(){
10837         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10838     },
10839
10840     // private
10841     initEvents : function(){
10842         
10843         this.createList();
10844         
10845         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10846         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10847         if(!this.multiple && this.showToggleBtn){
10848             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10849             if(this.hideTrigger){
10850                 this.trigger.setDisplayed(false);
10851             }
10852             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10853         }
10854         
10855         if(this.multiple){
10856             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10857         }
10858         
10859         if(this.removable && !this.editable && !this.tickable){
10860             var close = this.closeTriggerEl();
10861             
10862             if(close){
10863                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10864                 close.on('click', this.removeBtnClick, this, close);
10865             }
10866         }
10867         
10868         //this.trigger.addClassOnOver('x-form-trigger-over');
10869         //this.trigger.addClassOnClick('x-form-trigger-click');
10870         
10871         //if(!this.width){
10872         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10873         //}
10874     },
10875     
10876     closeTriggerEl : function()
10877     {
10878         var close = this.el.select('.roo-combo-removable-btn', true).first();
10879         return close ? close : false;
10880     },
10881     
10882     removeBtnClick : function(e, h, el)
10883     {
10884         e.preventDefault();
10885         
10886         if(this.fireEvent("remove", this) !== false){
10887             this.reset();
10888             this.fireEvent("afterremove", this)
10889         }
10890     },
10891     
10892     createList : function()
10893     {
10894         this.list = Roo.get(document.body).createChild({
10895             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10896             cls: 'typeahead typeahead-long dropdown-menu',
10897             style: 'display:none'
10898         });
10899         
10900         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10901         
10902     },
10903
10904     // private
10905     initTrigger : function(){
10906        
10907     },
10908
10909     // private
10910     onDestroy : function(){
10911         if(this.trigger){
10912             this.trigger.removeAllListeners();
10913           //  this.trigger.remove();
10914         }
10915         //if(this.wrap){
10916         //    this.wrap.remove();
10917         //}
10918         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10919     },
10920
10921     // private
10922     onFocus : function(){
10923         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10924         /*
10925         if(!this.mimicing){
10926             this.wrap.addClass('x-trigger-wrap-focus');
10927             this.mimicing = true;
10928             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10929             if(this.monitorTab){
10930                 this.el.on("keydown", this.checkTab, this);
10931             }
10932         }
10933         */
10934     },
10935
10936     // private
10937     checkTab : function(e){
10938         if(e.getKey() == e.TAB){
10939             this.triggerBlur();
10940         }
10941     },
10942
10943     // private
10944     onBlur : function(){
10945         // do nothing
10946     },
10947
10948     // private
10949     mimicBlur : function(e, t){
10950         /*
10951         if(!this.wrap.contains(t) && this.validateBlur()){
10952             this.triggerBlur();
10953         }
10954         */
10955     },
10956
10957     // private
10958     triggerBlur : function(){
10959         this.mimicing = false;
10960         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10961         if(this.monitorTab){
10962             this.el.un("keydown", this.checkTab, this);
10963         }
10964         //this.wrap.removeClass('x-trigger-wrap-focus');
10965         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10966     },
10967
10968     // private
10969     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10970     validateBlur : function(e, t){
10971         return true;
10972     },
10973
10974     // private
10975     onDisable : function(){
10976         this.inputEl().dom.disabled = true;
10977         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10978         //if(this.wrap){
10979         //    this.wrap.addClass('x-item-disabled');
10980         //}
10981     },
10982
10983     // private
10984     onEnable : function(){
10985         this.inputEl().dom.disabled = false;
10986         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10987         //if(this.wrap){
10988         //    this.el.removeClass('x-item-disabled');
10989         //}
10990     },
10991
10992     // private
10993     onShow : function(){
10994         var ae = this.getActionEl();
10995         
10996         if(ae){
10997             ae.dom.style.display = '';
10998             ae.dom.style.visibility = 'visible';
10999         }
11000     },
11001
11002     // private
11003     
11004     onHide : function(){
11005         var ae = this.getActionEl();
11006         ae.dom.style.display = 'none';
11007     },
11008
11009     /**
11010      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
11011      * by an implementing function.
11012      * @method
11013      * @param {EventObject} e
11014      */
11015     onTriggerClick : Roo.emptyFn
11016 });
11017  /*
11018  * Based on:
11019  * Ext JS Library 1.1.1
11020  * Copyright(c) 2006-2007, Ext JS, LLC.
11021  *
11022  * Originally Released Under LGPL - original licence link has changed is not relivant.
11023  *
11024  * Fork - LGPL
11025  * <script type="text/javascript">
11026  */
11027
11028
11029 /**
11030  * @class Roo.data.SortTypes
11031  * @singleton
11032  * Defines the default sorting (casting?) comparison functions used when sorting data.
11033  */
11034 Roo.data.SortTypes = {
11035     /**
11036      * Default sort that does nothing
11037      * @param {Mixed} s The value being converted
11038      * @return {Mixed} The comparison value
11039      */
11040     none : function(s){
11041         return s;
11042     },
11043     
11044     /**
11045      * The regular expression used to strip tags
11046      * @type {RegExp}
11047      * @property
11048      */
11049     stripTagsRE : /<\/?[^>]+>/gi,
11050     
11051     /**
11052      * Strips all HTML tags to sort on text only
11053      * @param {Mixed} s The value being converted
11054      * @return {String} The comparison value
11055      */
11056     asText : function(s){
11057         return String(s).replace(this.stripTagsRE, "");
11058     },
11059     
11060     /**
11061      * Strips all HTML tags to sort on text only - Case insensitive
11062      * @param {Mixed} s The value being converted
11063      * @return {String} The comparison value
11064      */
11065     asUCText : function(s){
11066         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11067     },
11068     
11069     /**
11070      * Case insensitive string
11071      * @param {Mixed} s The value being converted
11072      * @return {String} The comparison value
11073      */
11074     asUCString : function(s) {
11075         return String(s).toUpperCase();
11076     },
11077     
11078     /**
11079      * Date sorting
11080      * @param {Mixed} s The value being converted
11081      * @return {Number} The comparison value
11082      */
11083     asDate : function(s) {
11084         if(!s){
11085             return 0;
11086         }
11087         if(s instanceof Date){
11088             return s.getTime();
11089         }
11090         return Date.parse(String(s));
11091     },
11092     
11093     /**
11094      * Float sorting
11095      * @param {Mixed} s The value being converted
11096      * @return {Float} The comparison value
11097      */
11098     asFloat : function(s) {
11099         var val = parseFloat(String(s).replace(/,/g, ""));
11100         if(isNaN(val)) {
11101             val = 0;
11102         }
11103         return val;
11104     },
11105     
11106     /**
11107      * Integer sorting
11108      * @param {Mixed} s The value being converted
11109      * @return {Number} The comparison value
11110      */
11111     asInt : function(s) {
11112         var val = parseInt(String(s).replace(/,/g, ""));
11113         if(isNaN(val)) {
11114             val = 0;
11115         }
11116         return val;
11117     }
11118 };/*
11119  * Based on:
11120  * Ext JS Library 1.1.1
11121  * Copyright(c) 2006-2007, Ext JS, LLC.
11122  *
11123  * Originally Released Under LGPL - original licence link has changed is not relivant.
11124  *
11125  * Fork - LGPL
11126  * <script type="text/javascript">
11127  */
11128
11129 /**
11130 * @class Roo.data.Record
11131  * Instances of this class encapsulate both record <em>definition</em> information, and record
11132  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11133  * to access Records cached in an {@link Roo.data.Store} object.<br>
11134  * <p>
11135  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11136  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11137  * objects.<br>
11138  * <p>
11139  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11140  * @constructor
11141  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11142  * {@link #create}. The parameters are the same.
11143  * @param {Array} data An associative Array of data values keyed by the field name.
11144  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11145  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11146  * not specified an integer id is generated.
11147  */
11148 Roo.data.Record = function(data, id){
11149     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11150     this.data = data;
11151 };
11152
11153 /**
11154  * Generate a constructor for a specific record layout.
11155  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11156  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11157  * Each field definition object may contain the following properties: <ul>
11158  * <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,
11159  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11160  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11161  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11162  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11163  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11164  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11165  * this may be omitted.</p></li>
11166  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11167  * <ul><li>auto (Default, implies no conversion)</li>
11168  * <li>string</li>
11169  * <li>int</li>
11170  * <li>float</li>
11171  * <li>boolean</li>
11172  * <li>date</li></ul></p></li>
11173  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11174  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11175  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11176  * by the Reader into an object that will be stored in the Record. It is passed the
11177  * following parameters:<ul>
11178  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11179  * </ul></p></li>
11180  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11181  * </ul>
11182  * <br>usage:<br><pre><code>
11183 var TopicRecord = Roo.data.Record.create(
11184     {name: 'title', mapping: 'topic_title'},
11185     {name: 'author', mapping: 'username'},
11186     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11187     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11188     {name: 'lastPoster', mapping: 'user2'},
11189     {name: 'excerpt', mapping: 'post_text'}
11190 );
11191
11192 var myNewRecord = new TopicRecord({
11193     title: 'Do my job please',
11194     author: 'noobie',
11195     totalPosts: 1,
11196     lastPost: new Date(),
11197     lastPoster: 'Animal',
11198     excerpt: 'No way dude!'
11199 });
11200 myStore.add(myNewRecord);
11201 </code></pre>
11202  * @method create
11203  * @static
11204  */
11205 Roo.data.Record.create = function(o){
11206     var f = function(){
11207         f.superclass.constructor.apply(this, arguments);
11208     };
11209     Roo.extend(f, Roo.data.Record);
11210     var p = f.prototype;
11211     p.fields = new Roo.util.MixedCollection(false, function(field){
11212         return field.name;
11213     });
11214     for(var i = 0, len = o.length; i < len; i++){
11215         p.fields.add(new Roo.data.Field(o[i]));
11216     }
11217     f.getField = function(name){
11218         return p.fields.get(name);  
11219     };
11220     return f;
11221 };
11222
11223 Roo.data.Record.AUTO_ID = 1000;
11224 Roo.data.Record.EDIT = 'edit';
11225 Roo.data.Record.REJECT = 'reject';
11226 Roo.data.Record.COMMIT = 'commit';
11227
11228 Roo.data.Record.prototype = {
11229     /**
11230      * Readonly flag - true if this record has been modified.
11231      * @type Boolean
11232      */
11233     dirty : false,
11234     editing : false,
11235     error: null,
11236     modified: null,
11237
11238     // private
11239     join : function(store){
11240         this.store = store;
11241     },
11242
11243     /**
11244      * Set the named field to the specified value.
11245      * @param {String} name The name of the field to set.
11246      * @param {Object} value The value to set the field to.
11247      */
11248     set : function(name, value){
11249         if(this.data[name] == value){
11250             return;
11251         }
11252         this.dirty = true;
11253         if(!this.modified){
11254             this.modified = {};
11255         }
11256         if(typeof this.modified[name] == 'undefined'){
11257             this.modified[name] = this.data[name];
11258         }
11259         this.data[name] = value;
11260         if(!this.editing && this.store){
11261             this.store.afterEdit(this);
11262         }       
11263     },
11264
11265     /**
11266      * Get the value of the named field.
11267      * @param {String} name The name of the field to get the value of.
11268      * @return {Object} The value of the field.
11269      */
11270     get : function(name){
11271         return this.data[name]; 
11272     },
11273
11274     // private
11275     beginEdit : function(){
11276         this.editing = true;
11277         this.modified = {}; 
11278     },
11279
11280     // private
11281     cancelEdit : function(){
11282         this.editing = false;
11283         delete this.modified;
11284     },
11285
11286     // private
11287     endEdit : function(){
11288         this.editing = false;
11289         if(this.dirty && this.store){
11290             this.store.afterEdit(this);
11291         }
11292     },
11293
11294     /**
11295      * Usually called by the {@link Roo.data.Store} which owns the Record.
11296      * Rejects all changes made to the Record since either creation, or the last commit operation.
11297      * Modified fields are reverted to their original values.
11298      * <p>
11299      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11300      * of reject operations.
11301      */
11302     reject : function(){
11303         var m = this.modified;
11304         for(var n in m){
11305             if(typeof m[n] != "function"){
11306                 this.data[n] = m[n];
11307             }
11308         }
11309         this.dirty = false;
11310         delete this.modified;
11311         this.editing = false;
11312         if(this.store){
11313             this.store.afterReject(this);
11314         }
11315     },
11316
11317     /**
11318      * Usually called by the {@link Roo.data.Store} which owns the Record.
11319      * Commits all changes made to the Record since either creation, or the last commit operation.
11320      * <p>
11321      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11322      * of commit operations.
11323      */
11324     commit : function(){
11325         this.dirty = false;
11326         delete this.modified;
11327         this.editing = false;
11328         if(this.store){
11329             this.store.afterCommit(this);
11330         }
11331     },
11332
11333     // private
11334     hasError : function(){
11335         return this.error != null;
11336     },
11337
11338     // private
11339     clearError : function(){
11340         this.error = null;
11341     },
11342
11343     /**
11344      * Creates a copy of this record.
11345      * @param {String} id (optional) A new record id if you don't want to use this record's id
11346      * @return {Record}
11347      */
11348     copy : function(newId) {
11349         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11350     }
11351 };/*
11352  * Based on:
11353  * Ext JS Library 1.1.1
11354  * Copyright(c) 2006-2007, Ext JS, LLC.
11355  *
11356  * Originally Released Under LGPL - original licence link has changed is not relivant.
11357  *
11358  * Fork - LGPL
11359  * <script type="text/javascript">
11360  */
11361
11362
11363
11364 /**
11365  * @class Roo.data.Store
11366  * @extends Roo.util.Observable
11367  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11368  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11369  * <p>
11370  * 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
11371  * has no knowledge of the format of the data returned by the Proxy.<br>
11372  * <p>
11373  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11374  * instances from the data object. These records are cached and made available through accessor functions.
11375  * @constructor
11376  * Creates a new Store.
11377  * @param {Object} config A config object containing the objects needed for the Store to access data,
11378  * and read the data into Records.
11379  */
11380 Roo.data.Store = function(config){
11381     this.data = new Roo.util.MixedCollection(false);
11382     this.data.getKey = function(o){
11383         return o.id;
11384     };
11385     this.baseParams = {};
11386     // private
11387     this.paramNames = {
11388         "start" : "start",
11389         "limit" : "limit",
11390         "sort" : "sort",
11391         "dir" : "dir",
11392         "multisort" : "_multisort"
11393     };
11394
11395     if(config && config.data){
11396         this.inlineData = config.data;
11397         delete config.data;
11398     }
11399
11400     Roo.apply(this, config);
11401     
11402     if(this.reader){ // reader passed
11403         this.reader = Roo.factory(this.reader, Roo.data);
11404         this.reader.xmodule = this.xmodule || false;
11405         if(!this.recordType){
11406             this.recordType = this.reader.recordType;
11407         }
11408         if(this.reader.onMetaChange){
11409             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11410         }
11411     }
11412
11413     if(this.recordType){
11414         this.fields = this.recordType.prototype.fields;
11415     }
11416     this.modified = [];
11417
11418     this.addEvents({
11419         /**
11420          * @event datachanged
11421          * Fires when the data cache has changed, and a widget which is using this Store
11422          * as a Record cache should refresh its view.
11423          * @param {Store} this
11424          */
11425         datachanged : true,
11426         /**
11427          * @event metachange
11428          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11429          * @param {Store} this
11430          * @param {Object} meta The JSON metadata
11431          */
11432         metachange : true,
11433         /**
11434          * @event add
11435          * Fires when Records have been added to the Store
11436          * @param {Store} this
11437          * @param {Roo.data.Record[]} records The array of Records added
11438          * @param {Number} index The index at which the record(s) were added
11439          */
11440         add : true,
11441         /**
11442          * @event remove
11443          * Fires when a Record has been removed from the Store
11444          * @param {Store} this
11445          * @param {Roo.data.Record} record The Record that was removed
11446          * @param {Number} index The index at which the record was removed
11447          */
11448         remove : true,
11449         /**
11450          * @event update
11451          * Fires when a Record has been updated
11452          * @param {Store} this
11453          * @param {Roo.data.Record} record The Record that was updated
11454          * @param {String} operation The update operation being performed.  Value may be one of:
11455          * <pre><code>
11456  Roo.data.Record.EDIT
11457  Roo.data.Record.REJECT
11458  Roo.data.Record.COMMIT
11459          * </code></pre>
11460          */
11461         update : true,
11462         /**
11463          * @event clear
11464          * Fires when the data cache has been cleared.
11465          * @param {Store} this
11466          */
11467         clear : true,
11468         /**
11469          * @event beforeload
11470          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11471          * the load action will be canceled.
11472          * @param {Store} this
11473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11474          */
11475         beforeload : true,
11476         /**
11477          * @event beforeloadadd
11478          * Fires after a new set of Records has been loaded.
11479          * @param {Store} this
11480          * @param {Roo.data.Record[]} records The Records that were loaded
11481          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11482          */
11483         beforeloadadd : true,
11484         /**
11485          * @event load
11486          * Fires after a new set of Records has been loaded, before they are added to the store.
11487          * @param {Store} this
11488          * @param {Roo.data.Record[]} records The Records that were loaded
11489          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11490          * @params {Object} return from reader
11491          */
11492         load : true,
11493         /**
11494          * @event loadexception
11495          * Fires if an exception occurs in the Proxy during loading.
11496          * Called with the signature of the Proxy's "loadexception" event.
11497          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11498          * 
11499          * @param {Proxy} 
11500          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11501          * @param {Object} load options 
11502          * @param {Object} jsonData from your request (normally this contains the Exception)
11503          */
11504         loadexception : true
11505     });
11506     
11507     if(this.proxy){
11508         this.proxy = Roo.factory(this.proxy, Roo.data);
11509         this.proxy.xmodule = this.xmodule || false;
11510         this.relayEvents(this.proxy,  ["loadexception"]);
11511     }
11512     this.sortToggle = {};
11513     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11514
11515     Roo.data.Store.superclass.constructor.call(this);
11516
11517     if(this.inlineData){
11518         this.loadData(this.inlineData);
11519         delete this.inlineData;
11520     }
11521 };
11522
11523 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11524      /**
11525     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11526     * without a remote query - used by combo/forms at present.
11527     */
11528     
11529     /**
11530     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11531     */
11532     /**
11533     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11534     */
11535     /**
11536     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11537     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11538     */
11539     /**
11540     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11541     * on any HTTP request
11542     */
11543     /**
11544     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11545     */
11546     /**
11547     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11548     */
11549     multiSort: false,
11550     /**
11551     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11552     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11553     */
11554     remoteSort : false,
11555
11556     /**
11557     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11558      * loaded or when a record is removed. (defaults to false).
11559     */
11560     pruneModifiedRecords : false,
11561
11562     // private
11563     lastOptions : null,
11564
11565     /**
11566      * Add Records to the Store and fires the add event.
11567      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11568      */
11569     add : function(records){
11570         records = [].concat(records);
11571         for(var i = 0, len = records.length; i < len; i++){
11572             records[i].join(this);
11573         }
11574         var index = this.data.length;
11575         this.data.addAll(records);
11576         this.fireEvent("add", this, records, index);
11577     },
11578
11579     /**
11580      * Remove a Record from the Store and fires the remove event.
11581      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11582      */
11583     remove : function(record){
11584         var index = this.data.indexOf(record);
11585         this.data.removeAt(index);
11586  
11587         if(this.pruneModifiedRecords){
11588             this.modified.remove(record);
11589         }
11590         this.fireEvent("remove", this, record, index);
11591     },
11592
11593     /**
11594      * Remove all Records from the Store and fires the clear event.
11595      */
11596     removeAll : function(){
11597         this.data.clear();
11598         if(this.pruneModifiedRecords){
11599             this.modified = [];
11600         }
11601         this.fireEvent("clear", this);
11602     },
11603
11604     /**
11605      * Inserts Records to the Store at the given index and fires the add event.
11606      * @param {Number} index The start index at which to insert the passed Records.
11607      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11608      */
11609     insert : function(index, records){
11610         records = [].concat(records);
11611         for(var i = 0, len = records.length; i < len; i++){
11612             this.data.insert(index, records[i]);
11613             records[i].join(this);
11614         }
11615         this.fireEvent("add", this, records, index);
11616     },
11617
11618     /**
11619      * Get the index within the cache of the passed Record.
11620      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11621      * @return {Number} The index of the passed Record. Returns -1 if not found.
11622      */
11623     indexOf : function(record){
11624         return this.data.indexOf(record);
11625     },
11626
11627     /**
11628      * Get the index within the cache of the Record with the passed id.
11629      * @param {String} id The id of the Record to find.
11630      * @return {Number} The index of the Record. Returns -1 if not found.
11631      */
11632     indexOfId : function(id){
11633         return this.data.indexOfKey(id);
11634     },
11635
11636     /**
11637      * Get the Record with the specified id.
11638      * @param {String} id The id of the Record to find.
11639      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11640      */
11641     getById : function(id){
11642         return this.data.key(id);
11643     },
11644
11645     /**
11646      * Get the Record at the specified index.
11647      * @param {Number} index The index of the Record to find.
11648      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11649      */
11650     getAt : function(index){
11651         return this.data.itemAt(index);
11652     },
11653
11654     /**
11655      * Returns a range of Records between specified indices.
11656      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11657      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11658      * @return {Roo.data.Record[]} An array of Records
11659      */
11660     getRange : function(start, end){
11661         return this.data.getRange(start, end);
11662     },
11663
11664     // private
11665     storeOptions : function(o){
11666         o = Roo.apply({}, o);
11667         delete o.callback;
11668         delete o.scope;
11669         this.lastOptions = o;
11670     },
11671
11672     /**
11673      * Loads the Record cache from the configured Proxy using the configured Reader.
11674      * <p>
11675      * If using remote paging, then the first load call must specify the <em>start</em>
11676      * and <em>limit</em> properties in the options.params property to establish the initial
11677      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11678      * <p>
11679      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11680      * and this call will return before the new data has been loaded. Perform any post-processing
11681      * in a callback function, or in a "load" event handler.</strong>
11682      * <p>
11683      * @param {Object} options An object containing properties which control loading options:<ul>
11684      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11685      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11686      * passed the following arguments:<ul>
11687      * <li>r : Roo.data.Record[]</li>
11688      * <li>options: Options object from the load call</li>
11689      * <li>success: Boolean success indicator</li></ul></li>
11690      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11691      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11692      * </ul>
11693      */
11694     load : function(options){
11695         options = options || {};
11696         if(this.fireEvent("beforeload", this, options) !== false){
11697             this.storeOptions(options);
11698             var p = Roo.apply(options.params || {}, this.baseParams);
11699             // if meta was not loaded from remote source.. try requesting it.
11700             if (!this.reader.metaFromRemote) {
11701                 p._requestMeta = 1;
11702             }
11703             if(this.sortInfo && this.remoteSort){
11704                 var pn = this.paramNames;
11705                 p[pn["sort"]] = this.sortInfo.field;
11706                 p[pn["dir"]] = this.sortInfo.direction;
11707             }
11708             if (this.multiSort) {
11709                 var pn = this.paramNames;
11710                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11711             }
11712             
11713             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11714         }
11715     },
11716
11717     /**
11718      * Reloads the Record cache from the configured Proxy using the configured Reader and
11719      * the options from the last load operation performed.
11720      * @param {Object} options (optional) An object containing properties which may override the options
11721      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11722      * the most recently used options are reused).
11723      */
11724     reload : function(options){
11725         this.load(Roo.applyIf(options||{}, this.lastOptions));
11726     },
11727
11728     // private
11729     // Called as a callback by the Reader during a load operation.
11730     loadRecords : function(o, options, success){
11731         if(!o || success === false){
11732             if(success !== false){
11733                 this.fireEvent("load", this, [], options, o);
11734             }
11735             if(options.callback){
11736                 options.callback.call(options.scope || this, [], options, false);
11737             }
11738             return;
11739         }
11740         // if data returned failure - throw an exception.
11741         if (o.success === false) {
11742             // show a message if no listener is registered.
11743             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11744                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11745             }
11746             // loadmask wil be hooked into this..
11747             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11748             return;
11749         }
11750         var r = o.records, t = o.totalRecords || r.length;
11751         
11752         this.fireEvent("beforeloadadd", this, r, options, o);
11753         
11754         if(!options || options.add !== true){
11755             if(this.pruneModifiedRecords){
11756                 this.modified = [];
11757             }
11758             for(var i = 0, len = r.length; i < len; i++){
11759                 r[i].join(this);
11760             }
11761             if(this.snapshot){
11762                 this.data = this.snapshot;
11763                 delete this.snapshot;
11764             }
11765             this.data.clear();
11766             this.data.addAll(r);
11767             this.totalLength = t;
11768             this.applySort();
11769             this.fireEvent("datachanged", this);
11770         }else{
11771             this.totalLength = Math.max(t, this.data.length+r.length);
11772             this.add(r);
11773         }
11774         
11775         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11776                 
11777             var e = new Roo.data.Record({});
11778
11779             e.set(this.parent.displayField, this.parent.emptyTitle);
11780             e.set(this.parent.valueField, '');
11781
11782             this.insert(0, e);
11783         }
11784             
11785         this.fireEvent("load", this, r, options, o);
11786         if(options.callback){
11787             options.callback.call(options.scope || this, r, options, true);
11788         }
11789     },
11790
11791
11792     /**
11793      * Loads data from a passed data block. A Reader which understands the format of the data
11794      * must have been configured in the constructor.
11795      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11796      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11797      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11798      */
11799     loadData : function(o, append){
11800         var r = this.reader.readRecords(o);
11801         this.loadRecords(r, {add: append}, true);
11802     },
11803
11804     /**
11805      * Gets the number of cached records.
11806      * <p>
11807      * <em>If using paging, this may not be the total size of the dataset. If the data object
11808      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11809      * the data set size</em>
11810      */
11811     getCount : function(){
11812         return this.data.length || 0;
11813     },
11814
11815     /**
11816      * Gets the total number of records in the dataset as returned by the server.
11817      * <p>
11818      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11819      * the dataset size</em>
11820      */
11821     getTotalCount : function(){
11822         return this.totalLength || 0;
11823     },
11824
11825     /**
11826      * Returns the sort state of the Store as an object with two properties:
11827      * <pre><code>
11828  field {String} The name of the field by which the Records are sorted
11829  direction {String} The sort order, "ASC" or "DESC"
11830      * </code></pre>
11831      */
11832     getSortState : function(){
11833         return this.sortInfo;
11834     },
11835
11836     // private
11837     applySort : function(){
11838         if(this.sortInfo && !this.remoteSort){
11839             var s = this.sortInfo, f = s.field;
11840             var st = this.fields.get(f).sortType;
11841             var fn = function(r1, r2){
11842                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11843                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11844             };
11845             this.data.sort(s.direction, fn);
11846             if(this.snapshot && this.snapshot != this.data){
11847                 this.snapshot.sort(s.direction, fn);
11848             }
11849         }
11850     },
11851
11852     /**
11853      * Sets the default sort column and order to be used by the next load operation.
11854      * @param {String} fieldName The name of the field to sort by.
11855      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11856      */
11857     setDefaultSort : function(field, dir){
11858         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11859     },
11860
11861     /**
11862      * Sort the Records.
11863      * If remote sorting is used, the sort is performed on the server, and the cache is
11864      * reloaded. If local sorting is used, the cache is sorted internally.
11865      * @param {String} fieldName The name of the field to sort by.
11866      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11867      */
11868     sort : function(fieldName, dir){
11869         var f = this.fields.get(fieldName);
11870         if(!dir){
11871             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11872             
11873             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11874                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11875             }else{
11876                 dir = f.sortDir;
11877             }
11878         }
11879         this.sortToggle[f.name] = dir;
11880         this.sortInfo = {field: f.name, direction: dir};
11881         if(!this.remoteSort){
11882             this.applySort();
11883             this.fireEvent("datachanged", this);
11884         }else{
11885             this.load(this.lastOptions);
11886         }
11887     },
11888
11889     /**
11890      * Calls the specified function for each of the Records in the cache.
11891      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11892      * Returning <em>false</em> aborts and exits the iteration.
11893      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11894      */
11895     each : function(fn, scope){
11896         this.data.each(fn, scope);
11897     },
11898
11899     /**
11900      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11901      * (e.g., during paging).
11902      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11903      */
11904     getModifiedRecords : function(){
11905         return this.modified;
11906     },
11907
11908     // private
11909     createFilterFn : function(property, value, anyMatch){
11910         if(!value.exec){ // not a regex
11911             value = String(value);
11912             if(value.length == 0){
11913                 return false;
11914             }
11915             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11916         }
11917         return function(r){
11918             return value.test(r.data[property]);
11919         };
11920     },
11921
11922     /**
11923      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11924      * @param {String} property A field on your records
11925      * @param {Number} start The record index to start at (defaults to 0)
11926      * @param {Number} end The last record index to include (defaults to length - 1)
11927      * @return {Number} The sum
11928      */
11929     sum : function(property, start, end){
11930         var rs = this.data.items, v = 0;
11931         start = start || 0;
11932         end = (end || end === 0) ? end : rs.length-1;
11933
11934         for(var i = start; i <= end; i++){
11935             v += (rs[i].data[property] || 0);
11936         }
11937         return v;
11938     },
11939
11940     /**
11941      * Filter the records by a specified property.
11942      * @param {String} field A field on your records
11943      * @param {String/RegExp} value Either a string that the field
11944      * should start with or a RegExp to test against the field
11945      * @param {Boolean} anyMatch True to match any part not just the beginning
11946      */
11947     filter : function(property, value, anyMatch){
11948         var fn = this.createFilterFn(property, value, anyMatch);
11949         return fn ? this.filterBy(fn) : this.clearFilter();
11950     },
11951
11952     /**
11953      * Filter by a function. The specified function will be called with each
11954      * record in this data source. If the function returns true the record is included,
11955      * otherwise it is filtered.
11956      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11957      * @param {Object} scope (optional) The scope of the function (defaults to this)
11958      */
11959     filterBy : function(fn, scope){
11960         this.snapshot = this.snapshot || this.data;
11961         this.data = this.queryBy(fn, scope||this);
11962         this.fireEvent("datachanged", this);
11963     },
11964
11965     /**
11966      * Query the records by a specified property.
11967      * @param {String} field A field on your records
11968      * @param {String/RegExp} value Either a string that the field
11969      * should start with or a RegExp to test against the field
11970      * @param {Boolean} anyMatch True to match any part not just the beginning
11971      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11972      */
11973     query : function(property, value, anyMatch){
11974         var fn = this.createFilterFn(property, value, anyMatch);
11975         return fn ? this.queryBy(fn) : this.data.clone();
11976     },
11977
11978     /**
11979      * Query by a function. The specified function will be called with each
11980      * record in this data source. If the function returns true the record is included
11981      * in the results.
11982      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11983      * @param {Object} scope (optional) The scope of the function (defaults to this)
11984       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11985      **/
11986     queryBy : function(fn, scope){
11987         var data = this.snapshot || this.data;
11988         return data.filterBy(fn, scope||this);
11989     },
11990
11991     /**
11992      * Collects unique values for a particular dataIndex from this store.
11993      * @param {String} dataIndex The property to collect
11994      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11995      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11996      * @return {Array} An array of the unique values
11997      **/
11998     collect : function(dataIndex, allowNull, bypassFilter){
11999         var d = (bypassFilter === true && this.snapshot) ?
12000                 this.snapshot.items : this.data.items;
12001         var v, sv, r = [], l = {};
12002         for(var i = 0, len = d.length; i < len; i++){
12003             v = d[i].data[dataIndex];
12004             sv = String(v);
12005             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12006                 l[sv] = true;
12007                 r[r.length] = v;
12008             }
12009         }
12010         return r;
12011     },
12012
12013     /**
12014      * Revert to a view of the Record cache with no filtering applied.
12015      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12016      */
12017     clearFilter : function(suppressEvent){
12018         if(this.snapshot && this.snapshot != this.data){
12019             this.data = this.snapshot;
12020             delete this.snapshot;
12021             if(suppressEvent !== true){
12022                 this.fireEvent("datachanged", this);
12023             }
12024         }
12025     },
12026
12027     // private
12028     afterEdit : function(record){
12029         if(this.modified.indexOf(record) == -1){
12030             this.modified.push(record);
12031         }
12032         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12033     },
12034     
12035     // private
12036     afterReject : function(record){
12037         this.modified.remove(record);
12038         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12039     },
12040
12041     // private
12042     afterCommit : function(record){
12043         this.modified.remove(record);
12044         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12045     },
12046
12047     /**
12048      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12049      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12050      */
12051     commitChanges : function(){
12052         var m = this.modified.slice(0);
12053         this.modified = [];
12054         for(var i = 0, len = m.length; i < len; i++){
12055             m[i].commit();
12056         }
12057     },
12058
12059     /**
12060      * Cancel outstanding changes on all changed records.
12061      */
12062     rejectChanges : function(){
12063         var m = this.modified.slice(0);
12064         this.modified = [];
12065         for(var i = 0, len = m.length; i < len; i++){
12066             m[i].reject();
12067         }
12068     },
12069
12070     onMetaChange : function(meta, rtype, o){
12071         this.recordType = rtype;
12072         this.fields = rtype.prototype.fields;
12073         delete this.snapshot;
12074         this.sortInfo = meta.sortInfo || this.sortInfo;
12075         this.modified = [];
12076         this.fireEvent('metachange', this, this.reader.meta);
12077     },
12078     
12079     moveIndex : function(data, type)
12080     {
12081         var index = this.indexOf(data);
12082         
12083         var newIndex = index + type;
12084         
12085         this.remove(data);
12086         
12087         this.insert(newIndex, data);
12088         
12089     }
12090 });/*
12091  * Based on:
12092  * Ext JS Library 1.1.1
12093  * Copyright(c) 2006-2007, Ext JS, LLC.
12094  *
12095  * Originally Released Under LGPL - original licence link has changed is not relivant.
12096  *
12097  * Fork - LGPL
12098  * <script type="text/javascript">
12099  */
12100
12101 /**
12102  * @class Roo.data.SimpleStore
12103  * @extends Roo.data.Store
12104  * Small helper class to make creating Stores from Array data easier.
12105  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12106  * @cfg {Array} fields An array of field definition objects, or field name strings.
12107  * @cfg {Array} data The multi-dimensional array of data
12108  * @constructor
12109  * @param {Object} config
12110  */
12111 Roo.data.SimpleStore = function(config){
12112     Roo.data.SimpleStore.superclass.constructor.call(this, {
12113         isLocal : true,
12114         reader: new Roo.data.ArrayReader({
12115                 id: config.id
12116             },
12117             Roo.data.Record.create(config.fields)
12118         ),
12119         proxy : new Roo.data.MemoryProxy(config.data)
12120     });
12121     this.load();
12122 };
12123 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12124  * Based on:
12125  * Ext JS Library 1.1.1
12126  * Copyright(c) 2006-2007, Ext JS, LLC.
12127  *
12128  * Originally Released Under LGPL - original licence link has changed is not relivant.
12129  *
12130  * Fork - LGPL
12131  * <script type="text/javascript">
12132  */
12133
12134 /**
12135 /**
12136  * @extends Roo.data.Store
12137  * @class Roo.data.JsonStore
12138  * Small helper class to make creating Stores for JSON data easier. <br/>
12139 <pre><code>
12140 var store = new Roo.data.JsonStore({
12141     url: 'get-images.php',
12142     root: 'images',
12143     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12144 });
12145 </code></pre>
12146  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12147  * JsonReader and HttpProxy (unless inline data is provided).</b>
12148  * @cfg {Array} fields An array of field definition objects, or field name strings.
12149  * @constructor
12150  * @param {Object} config
12151  */
12152 Roo.data.JsonStore = function(c){
12153     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12154         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12155         reader: new Roo.data.JsonReader(c, c.fields)
12156     }));
12157 };
12158 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12159  * Based on:
12160  * Ext JS Library 1.1.1
12161  * Copyright(c) 2006-2007, Ext JS, LLC.
12162  *
12163  * Originally Released Under LGPL - original licence link has changed is not relivant.
12164  *
12165  * Fork - LGPL
12166  * <script type="text/javascript">
12167  */
12168
12169  
12170 Roo.data.Field = function(config){
12171     if(typeof config == "string"){
12172         config = {name: config};
12173     }
12174     Roo.apply(this, config);
12175     
12176     if(!this.type){
12177         this.type = "auto";
12178     }
12179     
12180     var st = Roo.data.SortTypes;
12181     // named sortTypes are supported, here we look them up
12182     if(typeof this.sortType == "string"){
12183         this.sortType = st[this.sortType];
12184     }
12185     
12186     // set default sortType for strings and dates
12187     if(!this.sortType){
12188         switch(this.type){
12189             case "string":
12190                 this.sortType = st.asUCString;
12191                 break;
12192             case "date":
12193                 this.sortType = st.asDate;
12194                 break;
12195             default:
12196                 this.sortType = st.none;
12197         }
12198     }
12199
12200     // define once
12201     var stripRe = /[\$,%]/g;
12202
12203     // prebuilt conversion function for this field, instead of
12204     // switching every time we're reading a value
12205     if(!this.convert){
12206         var cv, dateFormat = this.dateFormat;
12207         switch(this.type){
12208             case "":
12209             case "auto":
12210             case undefined:
12211                 cv = function(v){ return v; };
12212                 break;
12213             case "string":
12214                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12215                 break;
12216             case "int":
12217                 cv = function(v){
12218                     return v !== undefined && v !== null && v !== '' ?
12219                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12220                     };
12221                 break;
12222             case "float":
12223                 cv = function(v){
12224                     return v !== undefined && v !== null && v !== '' ?
12225                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12226                     };
12227                 break;
12228             case "bool":
12229             case "boolean":
12230                 cv = function(v){ return v === true || v === "true" || v == 1; };
12231                 break;
12232             case "date":
12233                 cv = function(v){
12234                     if(!v){
12235                         return '';
12236                     }
12237                     if(v instanceof Date){
12238                         return v;
12239                     }
12240                     if(dateFormat){
12241                         if(dateFormat == "timestamp"){
12242                             return new Date(v*1000);
12243                         }
12244                         return Date.parseDate(v, dateFormat);
12245                     }
12246                     var parsed = Date.parse(v);
12247                     return parsed ? new Date(parsed) : null;
12248                 };
12249              break;
12250             
12251         }
12252         this.convert = cv;
12253     }
12254 };
12255
12256 Roo.data.Field.prototype = {
12257     dateFormat: null,
12258     defaultValue: "",
12259     mapping: null,
12260     sortType : null,
12261     sortDir : "ASC"
12262 };/*
12263  * Based on:
12264  * Ext JS Library 1.1.1
12265  * Copyright(c) 2006-2007, Ext JS, LLC.
12266  *
12267  * Originally Released Under LGPL - original licence link has changed is not relivant.
12268  *
12269  * Fork - LGPL
12270  * <script type="text/javascript">
12271  */
12272  
12273 // Base class for reading structured data from a data source.  This class is intended to be
12274 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12275
12276 /**
12277  * @class Roo.data.DataReader
12278  * Base class for reading structured data from a data source.  This class is intended to be
12279  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12280  */
12281
12282 Roo.data.DataReader = function(meta, recordType){
12283     
12284     this.meta = meta;
12285     
12286     this.recordType = recordType instanceof Array ? 
12287         Roo.data.Record.create(recordType) : recordType;
12288 };
12289
12290 Roo.data.DataReader.prototype = {
12291      /**
12292      * Create an empty record
12293      * @param {Object} data (optional) - overlay some values
12294      * @return {Roo.data.Record} record created.
12295      */
12296     newRow :  function(d) {
12297         var da =  {};
12298         this.recordType.prototype.fields.each(function(c) {
12299             switch( c.type) {
12300                 case 'int' : da[c.name] = 0; break;
12301                 case 'date' : da[c.name] = new Date(); break;
12302                 case 'float' : da[c.name] = 0.0; break;
12303                 case 'boolean' : da[c.name] = false; break;
12304                 default : da[c.name] = ""; break;
12305             }
12306             
12307         });
12308         return new this.recordType(Roo.apply(da, d));
12309     }
12310     
12311 };/*
12312  * Based on:
12313  * Ext JS Library 1.1.1
12314  * Copyright(c) 2006-2007, Ext JS, LLC.
12315  *
12316  * Originally Released Under LGPL - original licence link has changed is not relivant.
12317  *
12318  * Fork - LGPL
12319  * <script type="text/javascript">
12320  */
12321
12322 /**
12323  * @class Roo.data.DataProxy
12324  * @extends Roo.data.Observable
12325  * This class is an abstract base class for implementations which provide retrieval of
12326  * unformatted data objects.<br>
12327  * <p>
12328  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12329  * (of the appropriate type which knows how to parse the data object) to provide a block of
12330  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12331  * <p>
12332  * Custom implementations must implement the load method as described in
12333  * {@link Roo.data.HttpProxy#load}.
12334  */
12335 Roo.data.DataProxy = function(){
12336     this.addEvents({
12337         /**
12338          * @event beforeload
12339          * Fires before a network request is made to retrieve a data object.
12340          * @param {Object} This DataProxy object.
12341          * @param {Object} params The params parameter to the load function.
12342          */
12343         beforeload : true,
12344         /**
12345          * @event load
12346          * Fires before the load method's callback is called.
12347          * @param {Object} This DataProxy object.
12348          * @param {Object} o The data object.
12349          * @param {Object} arg The callback argument object passed to the load function.
12350          */
12351         load : true,
12352         /**
12353          * @event loadexception
12354          * Fires if an Exception occurs during data retrieval.
12355          * @param {Object} This DataProxy object.
12356          * @param {Object} o The data object.
12357          * @param {Object} arg The callback argument object passed to the load function.
12358          * @param {Object} e The Exception.
12359          */
12360         loadexception : true
12361     });
12362     Roo.data.DataProxy.superclass.constructor.call(this);
12363 };
12364
12365 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12366
12367     /**
12368      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12369      */
12370 /*
12371  * Based on:
12372  * Ext JS Library 1.1.1
12373  * Copyright(c) 2006-2007, Ext JS, LLC.
12374  *
12375  * Originally Released Under LGPL - original licence link has changed is not relivant.
12376  *
12377  * Fork - LGPL
12378  * <script type="text/javascript">
12379  */
12380 /**
12381  * @class Roo.data.MemoryProxy
12382  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12383  * to the Reader when its load method is called.
12384  * @constructor
12385  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12386  */
12387 Roo.data.MemoryProxy = function(data){
12388     if (data.data) {
12389         data = data.data;
12390     }
12391     Roo.data.MemoryProxy.superclass.constructor.call(this);
12392     this.data = data;
12393 };
12394
12395 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12396     
12397     /**
12398      * Load data from the requested source (in this case an in-memory
12399      * data object passed to the constructor), read the data object into
12400      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12401      * process that block using the passed callback.
12402      * @param {Object} params This parameter is not used by the MemoryProxy class.
12403      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12404      * object into a block of Roo.data.Records.
12405      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12406      * The function must be passed <ul>
12407      * <li>The Record block object</li>
12408      * <li>The "arg" argument from the load function</li>
12409      * <li>A boolean success indicator</li>
12410      * </ul>
12411      * @param {Object} scope The scope in which to call the callback
12412      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12413      */
12414     load : function(params, reader, callback, scope, arg){
12415         params = params || {};
12416         var result;
12417         try {
12418             result = reader.readRecords(params.data ? params.data :this.data);
12419         }catch(e){
12420             this.fireEvent("loadexception", this, arg, null, e);
12421             callback.call(scope, null, arg, false);
12422             return;
12423         }
12424         callback.call(scope, result, arg, true);
12425     },
12426     
12427     // private
12428     update : function(params, records){
12429         
12430     }
12431 });/*
12432  * Based on:
12433  * Ext JS Library 1.1.1
12434  * Copyright(c) 2006-2007, Ext JS, LLC.
12435  *
12436  * Originally Released Under LGPL - original licence link has changed is not relivant.
12437  *
12438  * Fork - LGPL
12439  * <script type="text/javascript">
12440  */
12441 /**
12442  * @class Roo.data.HttpProxy
12443  * @extends Roo.data.DataProxy
12444  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12445  * configured to reference a certain URL.<br><br>
12446  * <p>
12447  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12448  * from which the running page was served.<br><br>
12449  * <p>
12450  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12451  * <p>
12452  * Be aware that to enable the browser to parse an XML document, the server must set
12453  * the Content-Type header in the HTTP response to "text/xml".
12454  * @constructor
12455  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12456  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12457  * will be used to make the request.
12458  */
12459 Roo.data.HttpProxy = function(conn){
12460     Roo.data.HttpProxy.superclass.constructor.call(this);
12461     // is conn a conn config or a real conn?
12462     this.conn = conn;
12463     this.useAjax = !conn || !conn.events;
12464   
12465 };
12466
12467 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12468     // thse are take from connection...
12469     
12470     /**
12471      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12472      */
12473     /**
12474      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12475      * extra parameters to each request made by this object. (defaults to undefined)
12476      */
12477     /**
12478      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12479      *  to each request made by this object. (defaults to undefined)
12480      */
12481     /**
12482      * @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)
12483      */
12484     /**
12485      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12486      */
12487      /**
12488      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12489      * @type Boolean
12490      */
12491   
12492
12493     /**
12494      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12495      * @type Boolean
12496      */
12497     /**
12498      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12499      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12500      * a finer-grained basis than the DataProxy events.
12501      */
12502     getConnection : function(){
12503         return this.useAjax ? Roo.Ajax : this.conn;
12504     },
12505
12506     /**
12507      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12508      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12509      * process that block using the passed callback.
12510      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12511      * for the request to the remote server.
12512      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12513      * object into a block of Roo.data.Records.
12514      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12515      * The function must be passed <ul>
12516      * <li>The Record block object</li>
12517      * <li>The "arg" argument from the load function</li>
12518      * <li>A boolean success indicator</li>
12519      * </ul>
12520      * @param {Object} scope The scope in which to call the callback
12521      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12522      */
12523     load : function(params, reader, callback, scope, arg){
12524         if(this.fireEvent("beforeload", this, params) !== false){
12525             var  o = {
12526                 params : params || {},
12527                 request: {
12528                     callback : callback,
12529                     scope : scope,
12530                     arg : arg
12531                 },
12532                 reader: reader,
12533                 callback : this.loadResponse,
12534                 scope: this
12535             };
12536             if(this.useAjax){
12537                 Roo.applyIf(o, this.conn);
12538                 if(this.activeRequest){
12539                     Roo.Ajax.abort(this.activeRequest);
12540                 }
12541                 this.activeRequest = Roo.Ajax.request(o);
12542             }else{
12543                 this.conn.request(o);
12544             }
12545         }else{
12546             callback.call(scope||this, null, arg, false);
12547         }
12548     },
12549
12550     // private
12551     loadResponse : function(o, success, response){
12552         delete this.activeRequest;
12553         if(!success){
12554             this.fireEvent("loadexception", this, o, response);
12555             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12556             return;
12557         }
12558         var result;
12559         try {
12560             result = o.reader.read(response);
12561         }catch(e){
12562             this.fireEvent("loadexception", this, o, response, e);
12563             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12564             return;
12565         }
12566         
12567         this.fireEvent("load", this, o, o.request.arg);
12568         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12569     },
12570
12571     // private
12572     update : function(dataSet){
12573
12574     },
12575
12576     // private
12577     updateResponse : function(dataSet){
12578
12579     }
12580 });/*
12581  * Based on:
12582  * Ext JS Library 1.1.1
12583  * Copyright(c) 2006-2007, Ext JS, LLC.
12584  *
12585  * Originally Released Under LGPL - original licence link has changed is not relivant.
12586  *
12587  * Fork - LGPL
12588  * <script type="text/javascript">
12589  */
12590
12591 /**
12592  * @class Roo.data.ScriptTagProxy
12593  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12594  * other than the originating domain of the running page.<br><br>
12595  * <p>
12596  * <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
12597  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12598  * <p>
12599  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12600  * source code that is used as the source inside a &lt;script> tag.<br><br>
12601  * <p>
12602  * In order for the browser to process the returned data, the server must wrap the data object
12603  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12604  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12605  * depending on whether the callback name was passed:
12606  * <p>
12607  * <pre><code>
12608 boolean scriptTag = false;
12609 String cb = request.getParameter("callback");
12610 if (cb != null) {
12611     scriptTag = true;
12612     response.setContentType("text/javascript");
12613 } else {
12614     response.setContentType("application/x-json");
12615 }
12616 Writer out = response.getWriter();
12617 if (scriptTag) {
12618     out.write(cb + "(");
12619 }
12620 out.print(dataBlock.toJsonString());
12621 if (scriptTag) {
12622     out.write(");");
12623 }
12624 </pre></code>
12625  *
12626  * @constructor
12627  * @param {Object} config A configuration object.
12628  */
12629 Roo.data.ScriptTagProxy = function(config){
12630     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12631     Roo.apply(this, config);
12632     this.head = document.getElementsByTagName("head")[0];
12633 };
12634
12635 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12636
12637 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12638     /**
12639      * @cfg {String} url The URL from which to request the data object.
12640      */
12641     /**
12642      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12643      */
12644     timeout : 30000,
12645     /**
12646      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12647      * the server the name of the callback function set up by the load call to process the returned data object.
12648      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12649      * javascript output which calls this named function passing the data object as its only parameter.
12650      */
12651     callbackParam : "callback",
12652     /**
12653      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12654      * name to the request.
12655      */
12656     nocache : true,
12657
12658     /**
12659      * Load data from the configured URL, read the data object into
12660      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12661      * process that block using the passed callback.
12662      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12663      * for the request to the remote server.
12664      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12665      * object into a block of Roo.data.Records.
12666      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12667      * The function must be passed <ul>
12668      * <li>The Record block object</li>
12669      * <li>The "arg" argument from the load function</li>
12670      * <li>A boolean success indicator</li>
12671      * </ul>
12672      * @param {Object} scope The scope in which to call the callback
12673      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12674      */
12675     load : function(params, reader, callback, scope, arg){
12676         if(this.fireEvent("beforeload", this, params) !== false){
12677
12678             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12679
12680             var url = this.url;
12681             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12682             if(this.nocache){
12683                 url += "&_dc=" + (new Date().getTime());
12684             }
12685             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12686             var trans = {
12687                 id : transId,
12688                 cb : "stcCallback"+transId,
12689                 scriptId : "stcScript"+transId,
12690                 params : params,
12691                 arg : arg,
12692                 url : url,
12693                 callback : callback,
12694                 scope : scope,
12695                 reader : reader
12696             };
12697             var conn = this;
12698
12699             window[trans.cb] = function(o){
12700                 conn.handleResponse(o, trans);
12701             };
12702
12703             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12704
12705             if(this.autoAbort !== false){
12706                 this.abort();
12707             }
12708
12709             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12710
12711             var script = document.createElement("script");
12712             script.setAttribute("src", url);
12713             script.setAttribute("type", "text/javascript");
12714             script.setAttribute("id", trans.scriptId);
12715             this.head.appendChild(script);
12716
12717             this.trans = trans;
12718         }else{
12719             callback.call(scope||this, null, arg, false);
12720         }
12721     },
12722
12723     // private
12724     isLoading : function(){
12725         return this.trans ? true : false;
12726     },
12727
12728     /**
12729      * Abort the current server request.
12730      */
12731     abort : function(){
12732         if(this.isLoading()){
12733             this.destroyTrans(this.trans);
12734         }
12735     },
12736
12737     // private
12738     destroyTrans : function(trans, isLoaded){
12739         this.head.removeChild(document.getElementById(trans.scriptId));
12740         clearTimeout(trans.timeoutId);
12741         if(isLoaded){
12742             window[trans.cb] = undefined;
12743             try{
12744                 delete window[trans.cb];
12745             }catch(e){}
12746         }else{
12747             // if hasn't been loaded, wait for load to remove it to prevent script error
12748             window[trans.cb] = function(){
12749                 window[trans.cb] = undefined;
12750                 try{
12751                     delete window[trans.cb];
12752                 }catch(e){}
12753             };
12754         }
12755     },
12756
12757     // private
12758     handleResponse : function(o, trans){
12759         this.trans = false;
12760         this.destroyTrans(trans, true);
12761         var result;
12762         try {
12763             result = trans.reader.readRecords(o);
12764         }catch(e){
12765             this.fireEvent("loadexception", this, o, trans.arg, e);
12766             trans.callback.call(trans.scope||window, null, trans.arg, false);
12767             return;
12768         }
12769         this.fireEvent("load", this, o, trans.arg);
12770         trans.callback.call(trans.scope||window, result, trans.arg, true);
12771     },
12772
12773     // private
12774     handleFailure : function(trans){
12775         this.trans = false;
12776         this.destroyTrans(trans, false);
12777         this.fireEvent("loadexception", this, null, trans.arg);
12778         trans.callback.call(trans.scope||window, null, trans.arg, false);
12779     }
12780 });/*
12781  * Based on:
12782  * Ext JS Library 1.1.1
12783  * Copyright(c) 2006-2007, Ext JS, LLC.
12784  *
12785  * Originally Released Under LGPL - original licence link has changed is not relivant.
12786  *
12787  * Fork - LGPL
12788  * <script type="text/javascript">
12789  */
12790
12791 /**
12792  * @class Roo.data.JsonReader
12793  * @extends Roo.data.DataReader
12794  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12795  * based on mappings in a provided Roo.data.Record constructor.
12796  * 
12797  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12798  * in the reply previously. 
12799  * 
12800  * <p>
12801  * Example code:
12802  * <pre><code>
12803 var RecordDef = Roo.data.Record.create([
12804     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12805     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12806 ]);
12807 var myReader = new Roo.data.JsonReader({
12808     totalProperty: "results",    // The property which contains the total dataset size (optional)
12809     root: "rows",                // The property which contains an Array of row objects
12810     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12811 }, RecordDef);
12812 </code></pre>
12813  * <p>
12814  * This would consume a JSON file like this:
12815  * <pre><code>
12816 { 'results': 2, 'rows': [
12817     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12818     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12819 }
12820 </code></pre>
12821  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12822  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12823  * paged from the remote server.
12824  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12825  * @cfg {String} root name of the property which contains the Array of row objects.
12826  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12827  * @cfg {Array} fields Array of field definition objects
12828  * @constructor
12829  * Create a new JsonReader
12830  * @param {Object} meta Metadata configuration options
12831  * @param {Object} recordType Either an Array of field definition objects,
12832  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12833  */
12834 Roo.data.JsonReader = function(meta, recordType){
12835     
12836     meta = meta || {};
12837     // set some defaults:
12838     Roo.applyIf(meta, {
12839         totalProperty: 'total',
12840         successProperty : 'success',
12841         root : 'data',
12842         id : 'id'
12843     });
12844     
12845     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12846 };
12847 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12848     
12849     /**
12850      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12851      * Used by Store query builder to append _requestMeta to params.
12852      * 
12853      */
12854     metaFromRemote : false,
12855     /**
12856      * This method is only used by a DataProxy which has retrieved data from a remote server.
12857      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12858      * @return {Object} data A data block which is used by an Roo.data.Store object as
12859      * a cache of Roo.data.Records.
12860      */
12861     read : function(response){
12862         var json = response.responseText;
12863        
12864         var o = /* eval:var:o */ eval("("+json+")");
12865         if(!o) {
12866             throw {message: "JsonReader.read: Json object not found"};
12867         }
12868         
12869         if(o.metaData){
12870             
12871             delete this.ef;
12872             this.metaFromRemote = true;
12873             this.meta = o.metaData;
12874             this.recordType = Roo.data.Record.create(o.metaData.fields);
12875             this.onMetaChange(this.meta, this.recordType, o);
12876         }
12877         return this.readRecords(o);
12878     },
12879
12880     // private function a store will implement
12881     onMetaChange : function(meta, recordType, o){
12882
12883     },
12884
12885     /**
12886          * @ignore
12887          */
12888     simpleAccess: function(obj, subsc) {
12889         return obj[subsc];
12890     },
12891
12892         /**
12893          * @ignore
12894          */
12895     getJsonAccessor: function(){
12896         var re = /[\[\.]/;
12897         return function(expr) {
12898             try {
12899                 return(re.test(expr))
12900                     ? new Function("obj", "return obj." + expr)
12901                     : function(obj){
12902                         return obj[expr];
12903                     };
12904             } catch(e){}
12905             return Roo.emptyFn;
12906         };
12907     }(),
12908
12909     /**
12910      * Create a data block containing Roo.data.Records from an XML document.
12911      * @param {Object} o An object which contains an Array of row objects in the property specified
12912      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12913      * which contains the total size of the dataset.
12914      * @return {Object} data A data block which is used by an Roo.data.Store object as
12915      * a cache of Roo.data.Records.
12916      */
12917     readRecords : function(o){
12918         /**
12919          * After any data loads, the raw JSON data is available for further custom processing.
12920          * @type Object
12921          */
12922         this.o = o;
12923         var s = this.meta, Record = this.recordType,
12924             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12925
12926 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12927         if (!this.ef) {
12928             if(s.totalProperty) {
12929                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12930                 }
12931                 if(s.successProperty) {
12932                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12933                 }
12934                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12935                 if (s.id) {
12936                         var g = this.getJsonAccessor(s.id);
12937                         this.getId = function(rec) {
12938                                 var r = g(rec);  
12939                                 return (r === undefined || r === "") ? null : r;
12940                         };
12941                 } else {
12942                         this.getId = function(){return null;};
12943                 }
12944             this.ef = [];
12945             for(var jj = 0; jj < fl; jj++){
12946                 f = fi[jj];
12947                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12948                 this.ef[jj] = this.getJsonAccessor(map);
12949             }
12950         }
12951
12952         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12953         if(s.totalProperty){
12954             var vt = parseInt(this.getTotal(o), 10);
12955             if(!isNaN(vt)){
12956                 totalRecords = vt;
12957             }
12958         }
12959         if(s.successProperty){
12960             var vs = this.getSuccess(o);
12961             if(vs === false || vs === 'false'){
12962                 success = false;
12963             }
12964         }
12965         var records = [];
12966         for(var i = 0; i < c; i++){
12967                 var n = root[i];
12968             var values = {};
12969             var id = this.getId(n);
12970             for(var j = 0; j < fl; j++){
12971                 f = fi[j];
12972             var v = this.ef[j](n);
12973             if (!f.convert) {
12974                 Roo.log('missing convert for ' + f.name);
12975                 Roo.log(f);
12976                 continue;
12977             }
12978             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12979             }
12980             var record = new Record(values, id);
12981             record.json = n;
12982             records[i] = record;
12983         }
12984         return {
12985             raw : o,
12986             success : success,
12987             records : records,
12988             totalRecords : totalRecords
12989         };
12990     }
12991 });/*
12992  * Based on:
12993  * Ext JS Library 1.1.1
12994  * Copyright(c) 2006-2007, Ext JS, LLC.
12995  *
12996  * Originally Released Under LGPL - original licence link has changed is not relivant.
12997  *
12998  * Fork - LGPL
12999  * <script type="text/javascript">
13000  */
13001
13002 /**
13003  * @class Roo.data.ArrayReader
13004  * @extends Roo.data.DataReader
13005  * Data reader class to create an Array of Roo.data.Record objects from an Array.
13006  * Each element of that Array represents a row of data fields. The
13007  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13008  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13009  * <p>
13010  * Example code:.
13011  * <pre><code>
13012 var RecordDef = Roo.data.Record.create([
13013     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
13014     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
13015 ]);
13016 var myReader = new Roo.data.ArrayReader({
13017     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
13018 }, RecordDef);
13019 </code></pre>
13020  * <p>
13021  * This would consume an Array like this:
13022  * <pre><code>
13023 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13024   </code></pre>
13025  
13026  * @constructor
13027  * Create a new JsonReader
13028  * @param {Object} meta Metadata configuration options.
13029  * @param {Object|Array} recordType Either an Array of field definition objects
13030  * 
13031  * @cfg {Array} fields Array of field definition objects
13032  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13033  * as specified to {@link Roo.data.Record#create},
13034  * or an {@link Roo.data.Record} object
13035  *
13036  * 
13037  * created using {@link Roo.data.Record#create}.
13038  */
13039 Roo.data.ArrayReader = function(meta, recordType){
13040     
13041      
13042     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13043 };
13044
13045 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13046     /**
13047      * Create a data block containing Roo.data.Records from an XML document.
13048      * @param {Object} o An Array of row objects which represents the dataset.
13049      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13050      * a cache of Roo.data.Records.
13051      */
13052     readRecords : function(o){
13053         var sid = this.meta ? this.meta.id : null;
13054         var recordType = this.recordType, fields = recordType.prototype.fields;
13055         var records = [];
13056         var root = o;
13057             for(var i = 0; i < root.length; i++){
13058                     var n = root[i];
13059                 var values = {};
13060                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13061                 for(var j = 0, jlen = fields.length; j < jlen; j++){
13062                 var f = fields.items[j];
13063                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13064                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13065                 v = f.convert(v);
13066                 values[f.name] = v;
13067             }
13068                 var record = new recordType(values, id);
13069                 record.json = n;
13070                 records[records.length] = record;
13071             }
13072             return {
13073                 records : records,
13074                 totalRecords : records.length
13075             };
13076     }
13077 });/*
13078  * - LGPL
13079  * * 
13080  */
13081
13082 /**
13083  * @class Roo.bootstrap.ComboBox
13084  * @extends Roo.bootstrap.TriggerField
13085  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13086  * @cfg {Boolean} append (true|false) default false
13087  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13088  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13089  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13090  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13091  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13092  * @cfg {Boolean} animate default true
13093  * @cfg {Boolean} emptyResultText only for touch device
13094  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13095  * @cfg {String} emptyTitle default ''
13096  * @constructor
13097  * Create a new ComboBox.
13098  * @param {Object} config Configuration options
13099  */
13100 Roo.bootstrap.ComboBox = function(config){
13101     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13102     this.addEvents({
13103         /**
13104          * @event expand
13105          * Fires when the dropdown list is expanded
13106         * @param {Roo.bootstrap.ComboBox} combo This combo box
13107         */
13108         'expand' : true,
13109         /**
13110          * @event collapse
13111          * Fires when the dropdown list is collapsed
13112         * @param {Roo.bootstrap.ComboBox} combo This combo box
13113         */
13114         'collapse' : true,
13115         /**
13116          * @event beforeselect
13117          * Fires before a list item is selected. Return false to cancel the selection.
13118         * @param {Roo.bootstrap.ComboBox} combo This combo box
13119         * @param {Roo.data.Record} record The data record returned from the underlying store
13120         * @param {Number} index The index of the selected item in the dropdown list
13121         */
13122         'beforeselect' : true,
13123         /**
13124          * @event select
13125          * Fires when a list item is selected
13126         * @param {Roo.bootstrap.ComboBox} combo This combo box
13127         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13128         * @param {Number} index The index of the selected item in the dropdown list
13129         */
13130         'select' : true,
13131         /**
13132          * @event beforequery
13133          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13134          * The event object passed has these properties:
13135         * @param {Roo.bootstrap.ComboBox} combo This combo box
13136         * @param {String} query The query
13137         * @param {Boolean} forceAll true to force "all" query
13138         * @param {Boolean} cancel true to cancel the query
13139         * @param {Object} e The query event object
13140         */
13141         'beforequery': true,
13142          /**
13143          * @event add
13144          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13145         * @param {Roo.bootstrap.ComboBox} combo This combo box
13146         */
13147         'add' : true,
13148         /**
13149          * @event edit
13150          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13151         * @param {Roo.bootstrap.ComboBox} combo This combo box
13152         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13153         */
13154         'edit' : true,
13155         /**
13156          * @event remove
13157          * Fires when the remove value from the combobox array
13158         * @param {Roo.bootstrap.ComboBox} combo This combo box
13159         */
13160         'remove' : true,
13161         /**
13162          * @event afterremove
13163          * Fires when the remove value from the combobox array
13164         * @param {Roo.bootstrap.ComboBox} combo This combo box
13165         */
13166         'afterremove' : true,
13167         /**
13168          * @event specialfilter
13169          * Fires when specialfilter
13170             * @param {Roo.bootstrap.ComboBox} combo This combo box
13171             */
13172         'specialfilter' : true,
13173         /**
13174          * @event tick
13175          * Fires when tick the element
13176             * @param {Roo.bootstrap.ComboBox} combo This combo box
13177             */
13178         'tick' : true,
13179         /**
13180          * @event touchviewdisplay
13181          * Fires when touch view require special display (default is using displayField)
13182             * @param {Roo.bootstrap.ComboBox} combo This combo box
13183             * @param {Object} cfg set html .
13184             */
13185         'touchviewdisplay' : true
13186         
13187     });
13188     
13189     this.item = [];
13190     this.tickItems = [];
13191     
13192     this.selectedIndex = -1;
13193     if(this.mode == 'local'){
13194         if(config.queryDelay === undefined){
13195             this.queryDelay = 10;
13196         }
13197         if(config.minChars === undefined){
13198             this.minChars = 0;
13199         }
13200     }
13201 };
13202
13203 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13204      
13205     /**
13206      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13207      * rendering into an Roo.Editor, defaults to false)
13208      */
13209     /**
13210      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13211      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13212      */
13213     /**
13214      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13215      */
13216     /**
13217      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13218      * the dropdown list (defaults to undefined, with no header element)
13219      */
13220
13221      /**
13222      * @cfg {String/Roo.Template} tpl The template to use to render the output
13223      */
13224      
13225      /**
13226      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13227      */
13228     listWidth: undefined,
13229     /**
13230      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13231      * mode = 'remote' or 'text' if mode = 'local')
13232      */
13233     displayField: undefined,
13234     
13235     /**
13236      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13237      * mode = 'remote' or 'value' if mode = 'local'). 
13238      * Note: use of a valueField requires the user make a selection
13239      * in order for a value to be mapped.
13240      */
13241     valueField: undefined,
13242     /**
13243      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13244      */
13245     modalTitle : '',
13246     
13247     /**
13248      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13249      * field's data value (defaults to the underlying DOM element's name)
13250      */
13251     hiddenName: undefined,
13252     /**
13253      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13254      */
13255     listClass: '',
13256     /**
13257      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13258      */
13259     selectedClass: 'active',
13260     
13261     /**
13262      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13263      */
13264     shadow:'sides',
13265     /**
13266      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13267      * anchor positions (defaults to 'tl-bl')
13268      */
13269     listAlign: 'tl-bl?',
13270     /**
13271      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13272      */
13273     maxHeight: 300,
13274     /**
13275      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13276      * query specified by the allQuery config option (defaults to 'query')
13277      */
13278     triggerAction: 'query',
13279     /**
13280      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13281      * (defaults to 4, does not apply if editable = false)
13282      */
13283     minChars : 4,
13284     /**
13285      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13286      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13287      */
13288     typeAhead: false,
13289     /**
13290      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13291      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13292      */
13293     queryDelay: 500,
13294     /**
13295      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13296      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13297      */
13298     pageSize: 0,
13299     /**
13300      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13301      * when editable = true (defaults to false)
13302      */
13303     selectOnFocus:false,
13304     /**
13305      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13306      */
13307     queryParam: 'query',
13308     /**
13309      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13310      * when mode = 'remote' (defaults to 'Loading...')
13311      */
13312     loadingText: 'Loading...',
13313     /**
13314      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13315      */
13316     resizable: false,
13317     /**
13318      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13319      */
13320     handleHeight : 8,
13321     /**
13322      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13323      * traditional select (defaults to true)
13324      */
13325     editable: true,
13326     /**
13327      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13328      */
13329     allQuery: '',
13330     /**
13331      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13332      */
13333     mode: 'remote',
13334     /**
13335      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13336      * listWidth has a higher value)
13337      */
13338     minListWidth : 70,
13339     /**
13340      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13341      * allow the user to set arbitrary text into the field (defaults to false)
13342      */
13343     forceSelection:false,
13344     /**
13345      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13346      * if typeAhead = true (defaults to 250)
13347      */
13348     typeAheadDelay : 250,
13349     /**
13350      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13351      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13352      */
13353     valueNotFoundText : undefined,
13354     /**
13355      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13356      */
13357     blockFocus : false,
13358     
13359     /**
13360      * @cfg {Boolean} disableClear Disable showing of clear button.
13361      */
13362     disableClear : false,
13363     /**
13364      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13365      */
13366     alwaysQuery : false,
13367     
13368     /**
13369      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13370      */
13371     multiple : false,
13372     
13373     /**
13374      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13375      */
13376     invalidClass : "has-warning",
13377     
13378     /**
13379      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13380      */
13381     validClass : "has-success",
13382     
13383     /**
13384      * @cfg {Boolean} specialFilter (true|false) special filter default false
13385      */
13386     specialFilter : false,
13387     
13388     /**
13389      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13390      */
13391     mobileTouchView : true,
13392     
13393     /**
13394      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13395      */
13396     useNativeIOS : false,
13397     
13398     /**
13399      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13400      */
13401     mobile_restrict_height : false,
13402     
13403     ios_options : false,
13404     
13405     //private
13406     addicon : false,
13407     editicon: false,
13408     
13409     page: 0,
13410     hasQuery: false,
13411     append: false,
13412     loadNext: false,
13413     autoFocus : true,
13414     tickable : false,
13415     btnPosition : 'right',
13416     triggerList : true,
13417     showToggleBtn : true,
13418     animate : true,
13419     emptyResultText: 'Empty',
13420     triggerText : 'Select',
13421     emptyTitle : '',
13422     
13423     // element that contains real text value.. (when hidden is used..)
13424     
13425     getAutoCreate : function()
13426     {   
13427         var cfg = false;
13428         //render
13429         /*
13430          * Render classic select for iso
13431          */
13432         
13433         if(Roo.isIOS && this.useNativeIOS){
13434             cfg = this.getAutoCreateNativeIOS();
13435             return cfg;
13436         }
13437         
13438         /*
13439          * Touch Devices
13440          */
13441         
13442         if(Roo.isTouch && this.mobileTouchView){
13443             cfg = this.getAutoCreateTouchView();
13444             return cfg;;
13445         }
13446         
13447         /*
13448          *  Normal ComboBox
13449          */
13450         if(!this.tickable){
13451             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13452             return cfg;
13453         }
13454         
13455         /*
13456          *  ComboBox with tickable selections
13457          */
13458              
13459         var align = this.labelAlign || this.parentLabelAlign();
13460         
13461         cfg = {
13462             cls : 'form-group roo-combobox-tickable' //input-group
13463         };
13464         
13465         var btn_text_select = '';
13466         var btn_text_done = '';
13467         var btn_text_cancel = '';
13468         
13469         if (this.btn_text_show) {
13470             btn_text_select = 'Select';
13471             btn_text_done = 'Done';
13472             btn_text_cancel = 'Cancel'; 
13473         }
13474         
13475         var buttons = {
13476             tag : 'div',
13477             cls : 'tickable-buttons',
13478             cn : [
13479                 {
13480                     tag : 'button',
13481                     type : 'button',
13482                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13483                     //html : this.triggerText
13484                     html: btn_text_select
13485                 },
13486                 {
13487                     tag : 'button',
13488                     type : 'button',
13489                     name : 'ok',
13490                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13491                     //html : 'Done'
13492                     html: btn_text_done
13493                 },
13494                 {
13495                     tag : 'button',
13496                     type : 'button',
13497                     name : 'cancel',
13498                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13499                     //html : 'Cancel'
13500                     html: btn_text_cancel
13501                 }
13502             ]
13503         };
13504         
13505         if(this.editable){
13506             buttons.cn.unshift({
13507                 tag: 'input',
13508                 cls: 'roo-select2-search-field-input'
13509             });
13510         }
13511         
13512         var _this = this;
13513         
13514         Roo.each(buttons.cn, function(c){
13515             if (_this.size) {
13516                 c.cls += ' btn-' + _this.size;
13517             }
13518
13519             if (_this.disabled) {
13520                 c.disabled = true;
13521             }
13522         });
13523         
13524         var box = {
13525             tag: 'div',
13526             style : 'display: contents',
13527             cn: [
13528                 {
13529                     tag: 'input',
13530                     type : 'hidden',
13531                     cls: 'form-hidden-field'
13532                 },
13533                 {
13534                     tag: 'ul',
13535                     cls: 'roo-select2-choices',
13536                     cn:[
13537                         {
13538                             tag: 'li',
13539                             cls: 'roo-select2-search-field',
13540                             cn: [
13541                                 buttons
13542                             ]
13543                         }
13544                     ]
13545                 }
13546             ]
13547         };
13548         
13549         var combobox = {
13550             cls: 'roo-select2-container input-group roo-select2-container-multi',
13551             cn: [
13552                 
13553                 box
13554 //                {
13555 //                    tag: 'ul',
13556 //                    cls: 'typeahead typeahead-long dropdown-menu',
13557 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13558 //                }
13559             ]
13560         };
13561         
13562         if(this.hasFeedback && !this.allowBlank){
13563             
13564             var feedback = {
13565                 tag: 'span',
13566                 cls: 'glyphicon form-control-feedback'
13567             };
13568
13569             combobox.cn.push(feedback);
13570         }
13571         
13572         var indicator = {
13573             tag : 'i',
13574             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13575             tooltip : 'This field is required'
13576         };
13577         if (Roo.bootstrap.version == 4) {
13578             indicator = {
13579                 tag : 'i',
13580                 style : 'display:none'
13581             };
13582         }
13583         if (align ==='left' && this.fieldLabel.length) {
13584             
13585             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13586             
13587             cfg.cn = [
13588                 indicator,
13589                 {
13590                     tag: 'label',
13591                     'for' :  id,
13592                     cls : 'control-label col-form-label',
13593                     html : this.fieldLabel
13594
13595                 },
13596                 {
13597                     cls : "", 
13598                     cn: [
13599                         combobox
13600                     ]
13601                 }
13602
13603             ];
13604             
13605             var labelCfg = cfg.cn[1];
13606             var contentCfg = cfg.cn[2];
13607             
13608
13609             if(this.indicatorpos == 'right'){
13610                 
13611                 cfg.cn = [
13612                     {
13613                         tag: 'label',
13614                         'for' :  id,
13615                         cls : 'control-label col-form-label',
13616                         cn : [
13617                             {
13618                                 tag : 'span',
13619                                 html : this.fieldLabel
13620                             },
13621                             indicator
13622                         ]
13623                     },
13624                     {
13625                         cls : "",
13626                         cn: [
13627                             combobox
13628                         ]
13629                     }
13630
13631                 ];
13632                 
13633                 
13634                 
13635                 labelCfg = cfg.cn[0];
13636                 contentCfg = cfg.cn[1];
13637             
13638             }
13639             
13640             if(this.labelWidth > 12){
13641                 labelCfg.style = "width: " + this.labelWidth + 'px';
13642             }
13643             
13644             if(this.labelWidth < 13 && this.labelmd == 0){
13645                 this.labelmd = this.labelWidth;
13646             }
13647             
13648             if(this.labellg > 0){
13649                 labelCfg.cls += ' col-lg-' + this.labellg;
13650                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13651             }
13652             
13653             if(this.labelmd > 0){
13654                 labelCfg.cls += ' col-md-' + this.labelmd;
13655                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13656             }
13657             
13658             if(this.labelsm > 0){
13659                 labelCfg.cls += ' col-sm-' + this.labelsm;
13660                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13661             }
13662             
13663             if(this.labelxs > 0){
13664                 labelCfg.cls += ' col-xs-' + this.labelxs;
13665                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13666             }
13667                 
13668                 
13669         } else if ( this.fieldLabel.length) {
13670 //                Roo.log(" label");
13671                  cfg.cn = [
13672                    indicator,
13673                     {
13674                         tag: 'label',
13675                         //cls : 'input-group-addon',
13676                         html : this.fieldLabel
13677                     },
13678                     combobox
13679                 ];
13680                 
13681                 if(this.indicatorpos == 'right'){
13682                     cfg.cn = [
13683                         {
13684                             tag: 'label',
13685                             //cls : 'input-group-addon',
13686                             html : this.fieldLabel
13687                         },
13688                         indicator,
13689                         combobox
13690                     ];
13691                     
13692                 }
13693
13694         } else {
13695             
13696 //                Roo.log(" no label && no align");
13697                 cfg = combobox
13698                      
13699                 
13700         }
13701          
13702         var settings=this;
13703         ['xs','sm','md','lg'].map(function(size){
13704             if (settings[size]) {
13705                 cfg.cls += ' col-' + size + '-' + settings[size];
13706             }
13707         });
13708         
13709         return cfg;
13710         
13711     },
13712     
13713     _initEventsCalled : false,
13714     
13715     // private
13716     initEvents: function()
13717     {   
13718         if (this._initEventsCalled) { // as we call render... prevent looping...
13719             return;
13720         }
13721         this._initEventsCalled = true;
13722         
13723         if (!this.store) {
13724             throw "can not find store for combo";
13725         }
13726         
13727         this.indicator = this.indicatorEl();
13728         
13729         this.store = Roo.factory(this.store, Roo.data);
13730         this.store.parent = this;
13731         
13732         // if we are building from html. then this element is so complex, that we can not really
13733         // use the rendered HTML.
13734         // so we have to trash and replace the previous code.
13735         if (Roo.XComponent.build_from_html) {
13736             // remove this element....
13737             var e = this.el.dom, k=0;
13738             while (e ) { e = e.previousSibling;  ++k;}
13739
13740             this.el.remove();
13741             
13742             this.el=false;
13743             this.rendered = false;
13744             
13745             this.render(this.parent().getChildContainer(true), k);
13746         }
13747         
13748         if(Roo.isIOS && this.useNativeIOS){
13749             this.initIOSView();
13750             return;
13751         }
13752         
13753         /*
13754          * Touch Devices
13755          */
13756         
13757         if(Roo.isTouch && this.mobileTouchView){
13758             this.initTouchView();
13759             return;
13760         }
13761         
13762         if(this.tickable){
13763             this.initTickableEvents();
13764             return;
13765         }
13766         
13767         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13768         
13769         if(this.hiddenName){
13770             
13771             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13772             
13773             this.hiddenField.dom.value =
13774                 this.hiddenValue !== undefined ? this.hiddenValue :
13775                 this.value !== undefined ? this.value : '';
13776
13777             // prevent input submission
13778             this.el.dom.removeAttribute('name');
13779             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13780              
13781              
13782         }
13783         //if(Roo.isGecko){
13784         //    this.el.dom.setAttribute('autocomplete', 'off');
13785         //}
13786         
13787         var cls = 'x-combo-list';
13788         
13789         //this.list = new Roo.Layer({
13790         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13791         //});
13792         
13793         var _this = this;
13794         
13795         (function(){
13796             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13797             _this.list.setWidth(lw);
13798         }).defer(100);
13799         
13800         this.list.on('mouseover', this.onViewOver, this);
13801         this.list.on('mousemove', this.onViewMove, this);
13802         this.list.on('scroll', this.onViewScroll, this);
13803         
13804         /*
13805         this.list.swallowEvent('mousewheel');
13806         this.assetHeight = 0;
13807
13808         if(this.title){
13809             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13810             this.assetHeight += this.header.getHeight();
13811         }
13812
13813         this.innerList = this.list.createChild({cls:cls+'-inner'});
13814         this.innerList.on('mouseover', this.onViewOver, this);
13815         this.innerList.on('mousemove', this.onViewMove, this);
13816         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13817         
13818         if(this.allowBlank && !this.pageSize && !this.disableClear){
13819             this.footer = this.list.createChild({cls:cls+'-ft'});
13820             this.pageTb = new Roo.Toolbar(this.footer);
13821            
13822         }
13823         if(this.pageSize){
13824             this.footer = this.list.createChild({cls:cls+'-ft'});
13825             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13826                     {pageSize: this.pageSize});
13827             
13828         }
13829         
13830         if (this.pageTb && this.allowBlank && !this.disableClear) {
13831             var _this = this;
13832             this.pageTb.add(new Roo.Toolbar.Fill(), {
13833                 cls: 'x-btn-icon x-btn-clear',
13834                 text: '&#160;',
13835                 handler: function()
13836                 {
13837                     _this.collapse();
13838                     _this.clearValue();
13839                     _this.onSelect(false, -1);
13840                 }
13841             });
13842         }
13843         if (this.footer) {
13844             this.assetHeight += this.footer.getHeight();
13845         }
13846         */
13847             
13848         if(!this.tpl){
13849             this.tpl = Roo.bootstrap.version == 4 ?
13850                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13851                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13852         }
13853
13854         this.view = new Roo.View(this.list, this.tpl, {
13855             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13856         });
13857         //this.view.wrapEl.setDisplayed(false);
13858         this.view.on('click', this.onViewClick, this);
13859         
13860         
13861         this.store.on('beforeload', this.onBeforeLoad, this);
13862         this.store.on('load', this.onLoad, this);
13863         this.store.on('loadexception', this.onLoadException, this);
13864         /*
13865         if(this.resizable){
13866             this.resizer = new Roo.Resizable(this.list,  {
13867                pinned:true, handles:'se'
13868             });
13869             this.resizer.on('resize', function(r, w, h){
13870                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13871                 this.listWidth = w;
13872                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13873                 this.restrictHeight();
13874             }, this);
13875             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13876         }
13877         */
13878         if(!this.editable){
13879             this.editable = true;
13880             this.setEditable(false);
13881         }
13882         
13883         /*
13884         
13885         if (typeof(this.events.add.listeners) != 'undefined') {
13886             
13887             this.addicon = this.wrap.createChild(
13888                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13889        
13890             this.addicon.on('click', function(e) {
13891                 this.fireEvent('add', this);
13892             }, this);
13893         }
13894         if (typeof(this.events.edit.listeners) != 'undefined') {
13895             
13896             this.editicon = this.wrap.createChild(
13897                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13898             if (this.addicon) {
13899                 this.editicon.setStyle('margin-left', '40px');
13900             }
13901             this.editicon.on('click', function(e) {
13902                 
13903                 // we fire even  if inothing is selected..
13904                 this.fireEvent('edit', this, this.lastData );
13905                 
13906             }, this);
13907         }
13908         */
13909         
13910         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13911             "up" : function(e){
13912                 this.inKeyMode = true;
13913                 this.selectPrev();
13914             },
13915
13916             "down" : function(e){
13917                 if(!this.isExpanded()){
13918                     this.onTriggerClick();
13919                 }else{
13920                     this.inKeyMode = true;
13921                     this.selectNext();
13922                 }
13923             },
13924
13925             "enter" : function(e){
13926 //                this.onViewClick();
13927                 //return true;
13928                 this.collapse();
13929                 
13930                 if(this.fireEvent("specialkey", this, e)){
13931                     this.onViewClick(false);
13932                 }
13933                 
13934                 return true;
13935             },
13936
13937             "esc" : function(e){
13938                 this.collapse();
13939             },
13940
13941             "tab" : function(e){
13942                 this.collapse();
13943                 
13944                 if(this.fireEvent("specialkey", this, e)){
13945                     this.onViewClick(false);
13946                 }
13947                 
13948                 return true;
13949             },
13950
13951             scope : this,
13952
13953             doRelay : function(foo, bar, hname){
13954                 if(hname == 'down' || this.scope.isExpanded()){
13955                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13956                 }
13957                 return true;
13958             },
13959
13960             forceKeyDown: true
13961         });
13962         
13963         
13964         this.queryDelay = Math.max(this.queryDelay || 10,
13965                 this.mode == 'local' ? 10 : 250);
13966         
13967         
13968         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13969         
13970         if(this.typeAhead){
13971             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13972         }
13973         if(this.editable !== false){
13974             this.inputEl().on("keyup", this.onKeyUp, this);
13975         }
13976         if(this.forceSelection){
13977             this.inputEl().on('blur', this.doForce, this);
13978         }
13979         
13980         if(this.multiple){
13981             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13982             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13983         }
13984     },
13985     
13986     initTickableEvents: function()
13987     {   
13988         this.createList();
13989         
13990         if(this.hiddenName){
13991             
13992             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13993             
13994             this.hiddenField.dom.value =
13995                 this.hiddenValue !== undefined ? this.hiddenValue :
13996                 this.value !== undefined ? this.value : '';
13997
13998             // prevent input submission
13999             this.el.dom.removeAttribute('name');
14000             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14001              
14002              
14003         }
14004         
14005 //        this.list = this.el.select('ul.dropdown-menu',true).first();
14006         
14007         this.choices = this.el.select('ul.roo-select2-choices', true).first();
14008         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14009         if(this.triggerList){
14010             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14011         }
14012          
14013         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14014         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14015         
14016         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14017         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14018         
14019         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14020         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14021         
14022         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14023         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14024         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14025         
14026         this.okBtn.hide();
14027         this.cancelBtn.hide();
14028         
14029         var _this = this;
14030         
14031         (function(){
14032             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14033             _this.list.setWidth(lw);
14034         }).defer(100);
14035         
14036         this.list.on('mouseover', this.onViewOver, this);
14037         this.list.on('mousemove', this.onViewMove, this);
14038         
14039         this.list.on('scroll', this.onViewScroll, this);
14040         
14041         if(!this.tpl){
14042             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
14043                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14044         }
14045
14046         this.view = new Roo.View(this.list, this.tpl, {
14047             singleSelect:true,
14048             tickable:true,
14049             parent:this,
14050             store: this.store,
14051             selectedClass: this.selectedClass
14052         });
14053         
14054         //this.view.wrapEl.setDisplayed(false);
14055         this.view.on('click', this.onViewClick, this);
14056         
14057         
14058         
14059         this.store.on('beforeload', this.onBeforeLoad, this);
14060         this.store.on('load', this.onLoad, this);
14061         this.store.on('loadexception', this.onLoadException, this);
14062         
14063         if(this.editable){
14064             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14065                 "up" : function(e){
14066                     this.inKeyMode = true;
14067                     this.selectPrev();
14068                 },
14069
14070                 "down" : function(e){
14071                     this.inKeyMode = true;
14072                     this.selectNext();
14073                 },
14074
14075                 "enter" : function(e){
14076                     if(this.fireEvent("specialkey", this, e)){
14077                         this.onViewClick(false);
14078                     }
14079                     
14080                     return true;
14081                 },
14082
14083                 "esc" : function(e){
14084                     this.onTickableFooterButtonClick(e, false, false);
14085                 },
14086
14087                 "tab" : function(e){
14088                     this.fireEvent("specialkey", this, e);
14089                     
14090                     this.onTickableFooterButtonClick(e, false, false);
14091                     
14092                     return true;
14093                 },
14094
14095                 scope : this,
14096
14097                 doRelay : function(e, fn, key){
14098                     if(this.scope.isExpanded()){
14099                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14100                     }
14101                     return true;
14102                 },
14103
14104                 forceKeyDown: true
14105             });
14106         }
14107         
14108         this.queryDelay = Math.max(this.queryDelay || 10,
14109                 this.mode == 'local' ? 10 : 250);
14110         
14111         
14112         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14113         
14114         if(this.typeAhead){
14115             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14116         }
14117         
14118         if(this.editable !== false){
14119             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14120         }
14121         
14122         this.indicator = this.indicatorEl();
14123         
14124         if(this.indicator){
14125             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14126             this.indicator.hide();
14127         }
14128         
14129     },
14130
14131     onDestroy : function(){
14132         if(this.view){
14133             this.view.setStore(null);
14134             this.view.el.removeAllListeners();
14135             this.view.el.remove();
14136             this.view.purgeListeners();
14137         }
14138         if(this.list){
14139             this.list.dom.innerHTML  = '';
14140         }
14141         
14142         if(this.store){
14143             this.store.un('beforeload', this.onBeforeLoad, this);
14144             this.store.un('load', this.onLoad, this);
14145             this.store.un('loadexception', this.onLoadException, this);
14146         }
14147         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14148     },
14149
14150     // private
14151     fireKey : function(e){
14152         if(e.isNavKeyPress() && !this.list.isVisible()){
14153             this.fireEvent("specialkey", this, e);
14154         }
14155     },
14156
14157     // private
14158     onResize: function(w, h){
14159 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14160 //        
14161 //        if(typeof w != 'number'){
14162 //            // we do not handle it!?!?
14163 //            return;
14164 //        }
14165 //        var tw = this.trigger.getWidth();
14166 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14167 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14168 //        var x = w - tw;
14169 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14170 //            
14171 //        //this.trigger.setStyle('left', x+'px');
14172 //        
14173 //        if(this.list && this.listWidth === undefined){
14174 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14175 //            this.list.setWidth(lw);
14176 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14177 //        }
14178         
14179     
14180         
14181     },
14182
14183     /**
14184      * Allow or prevent the user from directly editing the field text.  If false is passed,
14185      * the user will only be able to select from the items defined in the dropdown list.  This method
14186      * is the runtime equivalent of setting the 'editable' config option at config time.
14187      * @param {Boolean} value True to allow the user to directly edit the field text
14188      */
14189     setEditable : function(value){
14190         if(value == this.editable){
14191             return;
14192         }
14193         this.editable = value;
14194         if(!value){
14195             this.inputEl().dom.setAttribute('readOnly', true);
14196             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14197             this.inputEl().addClass('x-combo-noedit');
14198         }else{
14199             this.inputEl().dom.setAttribute('readOnly', false);
14200             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14201             this.inputEl().removeClass('x-combo-noedit');
14202         }
14203     },
14204
14205     // private
14206     
14207     onBeforeLoad : function(combo,opts){
14208         if(!this.hasFocus){
14209             return;
14210         }
14211          if (!opts.add) {
14212             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14213          }
14214         this.restrictHeight();
14215         this.selectedIndex = -1;
14216     },
14217
14218     // private
14219     onLoad : function(){
14220         
14221         this.hasQuery = false;
14222         
14223         if(!this.hasFocus){
14224             return;
14225         }
14226         
14227         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14228             this.loading.hide();
14229         }
14230         
14231         if(this.store.getCount() > 0){
14232             
14233             this.expand();
14234             this.restrictHeight();
14235             if(this.lastQuery == this.allQuery){
14236                 if(this.editable && !this.tickable){
14237                     this.inputEl().dom.select();
14238                 }
14239                 
14240                 if(
14241                     !this.selectByValue(this.value, true) &&
14242                     this.autoFocus && 
14243                     (
14244                         !this.store.lastOptions ||
14245                         typeof(this.store.lastOptions.add) == 'undefined' || 
14246                         this.store.lastOptions.add != true
14247                     )
14248                 ){
14249                     this.select(0, true);
14250                 }
14251             }else{
14252                 if(this.autoFocus){
14253                     this.selectNext();
14254                 }
14255                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14256                     this.taTask.delay(this.typeAheadDelay);
14257                 }
14258             }
14259         }else{
14260             this.onEmptyResults();
14261         }
14262         
14263         //this.el.focus();
14264     },
14265     // private
14266     onLoadException : function()
14267     {
14268         this.hasQuery = false;
14269         
14270         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14271             this.loading.hide();
14272         }
14273         
14274         if(this.tickable && this.editable){
14275             return;
14276         }
14277         
14278         this.collapse();
14279         // only causes errors at present
14280         //Roo.log(this.store.reader.jsonData);
14281         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14282             // fixme
14283             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14284         //}
14285         
14286         
14287     },
14288     // private
14289     onTypeAhead : function(){
14290         if(this.store.getCount() > 0){
14291             var r = this.store.getAt(0);
14292             var newValue = r.data[this.displayField];
14293             var len = newValue.length;
14294             var selStart = this.getRawValue().length;
14295             
14296             if(selStart != len){
14297                 this.setRawValue(newValue);
14298                 this.selectText(selStart, newValue.length);
14299             }
14300         }
14301     },
14302
14303     // private
14304     onSelect : function(record, index){
14305         
14306         if(this.fireEvent('beforeselect', this, record, index) !== false){
14307         
14308             this.setFromData(index > -1 ? record.data : false);
14309             
14310             this.collapse();
14311             this.fireEvent('select', this, record, index);
14312         }
14313     },
14314
14315     /**
14316      * Returns the currently selected field value or empty string if no value is set.
14317      * @return {String} value The selected value
14318      */
14319     getValue : function()
14320     {
14321         if(Roo.isIOS && this.useNativeIOS){
14322             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14323         }
14324         
14325         if(this.multiple){
14326             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14327         }
14328         
14329         if(this.valueField){
14330             return typeof this.value != 'undefined' ? this.value : '';
14331         }else{
14332             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14333         }
14334     },
14335     
14336     getRawValue : function()
14337     {
14338         if(Roo.isIOS && this.useNativeIOS){
14339             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14340         }
14341         
14342         var v = this.inputEl().getValue();
14343         
14344         return v;
14345     },
14346
14347     /**
14348      * Clears any text/value currently set in the field
14349      */
14350     clearValue : function(){
14351         
14352         if(this.hiddenField){
14353             this.hiddenField.dom.value = '';
14354         }
14355         this.value = '';
14356         this.setRawValue('');
14357         this.lastSelectionText = '';
14358         this.lastData = false;
14359         
14360         var close = this.closeTriggerEl();
14361         
14362         if(close){
14363             close.hide();
14364         }
14365         
14366         this.validate();
14367         
14368     },
14369
14370     /**
14371      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14372      * will be displayed in the field.  If the value does not match the data value of an existing item,
14373      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14374      * Otherwise the field will be blank (although the value will still be set).
14375      * @param {String} value The value to match
14376      */
14377     setValue : function(v)
14378     {
14379         if(Roo.isIOS && this.useNativeIOS){
14380             this.setIOSValue(v);
14381             return;
14382         }
14383         
14384         if(this.multiple){
14385             this.syncValue();
14386             return;
14387         }
14388         
14389         var text = v;
14390         if(this.valueField){
14391             var r = this.findRecord(this.valueField, v);
14392             if(r){
14393                 text = r.data[this.displayField];
14394             }else if(this.valueNotFoundText !== undefined){
14395                 text = this.valueNotFoundText;
14396             }
14397         }
14398         this.lastSelectionText = text;
14399         if(this.hiddenField){
14400             this.hiddenField.dom.value = v;
14401         }
14402         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14403         this.value = v;
14404         
14405         var close = this.closeTriggerEl();
14406         
14407         if(close){
14408             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14409         }
14410         
14411         this.validate();
14412     },
14413     /**
14414      * @property {Object} the last set data for the element
14415      */
14416     
14417     lastData : false,
14418     /**
14419      * Sets the value of the field based on a object which is related to the record format for the store.
14420      * @param {Object} value the value to set as. or false on reset?
14421      */
14422     setFromData : function(o){
14423         
14424         if(this.multiple){
14425             this.addItem(o);
14426             return;
14427         }
14428             
14429         var dv = ''; // display value
14430         var vv = ''; // value value..
14431         this.lastData = o;
14432         if (this.displayField) {
14433             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14434         } else {
14435             // this is an error condition!!!
14436             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14437         }
14438         
14439         if(this.valueField){
14440             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14441         }
14442         
14443         var close = this.closeTriggerEl();
14444         
14445         if(close){
14446             if(dv.length || vv * 1 > 0){
14447                 close.show() ;
14448                 this.blockFocus=true;
14449             } else {
14450                 close.hide();
14451             }             
14452         }
14453         
14454         if(this.hiddenField){
14455             this.hiddenField.dom.value = vv;
14456             
14457             this.lastSelectionText = dv;
14458             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14459             this.value = vv;
14460             return;
14461         }
14462         // no hidden field.. - we store the value in 'value', but still display
14463         // display field!!!!
14464         this.lastSelectionText = dv;
14465         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14466         this.value = vv;
14467         
14468         
14469         
14470     },
14471     // private
14472     reset : function(){
14473         // overridden so that last data is reset..
14474         
14475         if(this.multiple){
14476             this.clearItem();
14477             return;
14478         }
14479         
14480         this.setValue(this.originalValue);
14481         //this.clearInvalid();
14482         this.lastData = false;
14483         if (this.view) {
14484             this.view.clearSelections();
14485         }
14486         
14487         this.validate();
14488     },
14489     // private
14490     findRecord : function(prop, value){
14491         var record;
14492         if(this.store.getCount() > 0){
14493             this.store.each(function(r){
14494                 if(r.data[prop] == value){
14495                     record = r;
14496                     return false;
14497                 }
14498                 return true;
14499             });
14500         }
14501         return record;
14502     },
14503     
14504     getName: function()
14505     {
14506         // returns hidden if it's set..
14507         if (!this.rendered) {return ''};
14508         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14509         
14510     },
14511     // private
14512     onViewMove : function(e, t){
14513         this.inKeyMode = false;
14514     },
14515
14516     // private
14517     onViewOver : function(e, t){
14518         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14519             return;
14520         }
14521         var item = this.view.findItemFromChild(t);
14522         
14523         if(item){
14524             var index = this.view.indexOf(item);
14525             this.select(index, false);
14526         }
14527     },
14528
14529     // private
14530     onViewClick : function(view, doFocus, el, e)
14531     {
14532         var index = this.view.getSelectedIndexes()[0];
14533         
14534         var r = this.store.getAt(index);
14535         
14536         if(this.tickable){
14537             
14538             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14539                 return;
14540             }
14541             
14542             var rm = false;
14543             var _this = this;
14544             
14545             Roo.each(this.tickItems, function(v,k){
14546                 
14547                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14548                     Roo.log(v);
14549                     _this.tickItems.splice(k, 1);
14550                     
14551                     if(typeof(e) == 'undefined' && view == false){
14552                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14553                     }
14554                     
14555                     rm = true;
14556                     return;
14557                 }
14558             });
14559             
14560             if(rm){
14561                 return;
14562             }
14563             
14564             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14565                 this.tickItems.push(r.data);
14566             }
14567             
14568             if(typeof(e) == 'undefined' && view == false){
14569                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14570             }
14571                     
14572             return;
14573         }
14574         
14575         if(r){
14576             this.onSelect(r, index);
14577         }
14578         if(doFocus !== false && !this.blockFocus){
14579             this.inputEl().focus();
14580         }
14581     },
14582
14583     // private
14584     restrictHeight : function(){
14585         //this.innerList.dom.style.height = '';
14586         //var inner = this.innerList.dom;
14587         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14588         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14589         //this.list.beginUpdate();
14590         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14591         this.list.alignTo(this.inputEl(), this.listAlign);
14592         this.list.alignTo(this.inputEl(), this.listAlign);
14593         //this.list.endUpdate();
14594     },
14595
14596     // private
14597     onEmptyResults : function(){
14598         
14599         if(this.tickable && this.editable){
14600             this.hasFocus = false;
14601             this.restrictHeight();
14602             return;
14603         }
14604         
14605         this.collapse();
14606     },
14607
14608     /**
14609      * Returns true if the dropdown list is expanded, else false.
14610      */
14611     isExpanded : function(){
14612         return this.list.isVisible();
14613     },
14614
14615     /**
14616      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14617      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14618      * @param {String} value The data value of the item to select
14619      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14620      * selected item if it is not currently in view (defaults to true)
14621      * @return {Boolean} True if the value matched an item in the list, else false
14622      */
14623     selectByValue : function(v, scrollIntoView){
14624         if(v !== undefined && v !== null){
14625             var r = this.findRecord(this.valueField || this.displayField, v);
14626             if(r){
14627                 this.select(this.store.indexOf(r), scrollIntoView);
14628                 return true;
14629             }
14630         }
14631         return false;
14632     },
14633
14634     /**
14635      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14636      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14637      * @param {Number} index The zero-based index of the list item to select
14638      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14639      * selected item if it is not currently in view (defaults to true)
14640      */
14641     select : function(index, scrollIntoView){
14642         this.selectedIndex = index;
14643         this.view.select(index);
14644         if(scrollIntoView !== false){
14645             var el = this.view.getNode(index);
14646             /*
14647              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14648              */
14649             if(el){
14650                 this.list.scrollChildIntoView(el, false);
14651             }
14652         }
14653     },
14654
14655     // private
14656     selectNext : function(){
14657         var ct = this.store.getCount();
14658         if(ct > 0){
14659             if(this.selectedIndex == -1){
14660                 this.select(0);
14661             }else if(this.selectedIndex < ct-1){
14662                 this.select(this.selectedIndex+1);
14663             }
14664         }
14665     },
14666
14667     // private
14668     selectPrev : function(){
14669         var ct = this.store.getCount();
14670         if(ct > 0){
14671             if(this.selectedIndex == -1){
14672                 this.select(0);
14673             }else if(this.selectedIndex != 0){
14674                 this.select(this.selectedIndex-1);
14675             }
14676         }
14677     },
14678
14679     // private
14680     onKeyUp : function(e){
14681         if(this.editable !== false && !e.isSpecialKey()){
14682             this.lastKey = e.getKey();
14683             this.dqTask.delay(this.queryDelay);
14684         }
14685     },
14686
14687     // private
14688     validateBlur : function(){
14689         return !this.list || !this.list.isVisible();   
14690     },
14691
14692     // private
14693     initQuery : function(){
14694         
14695         var v = this.getRawValue();
14696         
14697         if(this.tickable && this.editable){
14698             v = this.tickableInputEl().getValue();
14699         }
14700         
14701         this.doQuery(v);
14702     },
14703
14704     // private
14705     doForce : function(){
14706         if(this.inputEl().dom.value.length > 0){
14707             this.inputEl().dom.value =
14708                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14709              
14710         }
14711     },
14712
14713     /**
14714      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14715      * query allowing the query action to be canceled if needed.
14716      * @param {String} query The SQL query to execute
14717      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14718      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14719      * saved in the current store (defaults to false)
14720      */
14721     doQuery : function(q, forceAll){
14722         
14723         if(q === undefined || q === null){
14724             q = '';
14725         }
14726         var qe = {
14727             query: q,
14728             forceAll: forceAll,
14729             combo: this,
14730             cancel:false
14731         };
14732         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14733             return false;
14734         }
14735         q = qe.query;
14736         
14737         forceAll = qe.forceAll;
14738         if(forceAll === true || (q.length >= this.minChars)){
14739             
14740             this.hasQuery = true;
14741             
14742             if(this.lastQuery != q || this.alwaysQuery){
14743                 this.lastQuery = q;
14744                 if(this.mode == 'local'){
14745                     this.selectedIndex = -1;
14746                     if(forceAll){
14747                         this.store.clearFilter();
14748                     }else{
14749                         
14750                         if(this.specialFilter){
14751                             this.fireEvent('specialfilter', this);
14752                             this.onLoad();
14753                             return;
14754                         }
14755                         
14756                         this.store.filter(this.displayField, q);
14757                     }
14758                     
14759                     this.store.fireEvent("datachanged", this.store);
14760                     
14761                     this.onLoad();
14762                     
14763                     
14764                 }else{
14765                     
14766                     this.store.baseParams[this.queryParam] = q;
14767                     
14768                     var options = {params : this.getParams(q)};
14769                     
14770                     if(this.loadNext){
14771                         options.add = true;
14772                         options.params.start = this.page * this.pageSize;
14773                     }
14774                     
14775                     this.store.load(options);
14776                     
14777                     /*
14778                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14779                      *  we should expand the list on onLoad
14780                      *  so command out it
14781                      */
14782 //                    this.expand();
14783                 }
14784             }else{
14785                 this.selectedIndex = -1;
14786                 this.onLoad();   
14787             }
14788         }
14789         
14790         this.loadNext = false;
14791     },
14792     
14793     // private
14794     getParams : function(q){
14795         var p = {};
14796         //p[this.queryParam] = q;
14797         
14798         if(this.pageSize){
14799             p.start = 0;
14800             p.limit = this.pageSize;
14801         }
14802         return p;
14803     },
14804
14805     /**
14806      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14807      */
14808     collapse : function(){
14809         if(!this.isExpanded()){
14810             return;
14811         }
14812         
14813         this.list.hide();
14814         
14815         this.hasFocus = false;
14816         
14817         if(this.tickable){
14818             this.okBtn.hide();
14819             this.cancelBtn.hide();
14820             this.trigger.show();
14821             
14822             if(this.editable){
14823                 this.tickableInputEl().dom.value = '';
14824                 this.tickableInputEl().blur();
14825             }
14826             
14827         }
14828         
14829         Roo.get(document).un('mousedown', this.collapseIf, this);
14830         Roo.get(document).un('mousewheel', this.collapseIf, this);
14831         if (!this.editable) {
14832             Roo.get(document).un('keydown', this.listKeyPress, this);
14833         }
14834         this.fireEvent('collapse', this);
14835         
14836         this.validate();
14837     },
14838
14839     // private
14840     collapseIf : function(e){
14841         var in_combo  = e.within(this.el);
14842         var in_list =  e.within(this.list);
14843         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14844         
14845         if (in_combo || in_list || is_list) {
14846             //e.stopPropagation();
14847             return;
14848         }
14849         
14850         if(this.tickable){
14851             this.onTickableFooterButtonClick(e, false, false);
14852         }
14853
14854         this.collapse();
14855         
14856     },
14857
14858     /**
14859      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14860      */
14861     expand : function(){
14862        
14863         if(this.isExpanded() || !this.hasFocus){
14864             return;
14865         }
14866         
14867         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14868         this.list.setWidth(lw);
14869         
14870         Roo.log('expand');
14871         
14872         this.list.show();
14873         
14874         this.restrictHeight();
14875         
14876         if(this.tickable){
14877             
14878             this.tickItems = Roo.apply([], this.item);
14879             
14880             this.okBtn.show();
14881             this.cancelBtn.show();
14882             this.trigger.hide();
14883             
14884             if(this.editable){
14885                 this.tickableInputEl().focus();
14886             }
14887             
14888         }
14889         
14890         Roo.get(document).on('mousedown', this.collapseIf, this);
14891         Roo.get(document).on('mousewheel', this.collapseIf, this);
14892         if (!this.editable) {
14893             Roo.get(document).on('keydown', this.listKeyPress, this);
14894         }
14895         
14896         this.fireEvent('expand', this);
14897     },
14898
14899     // private
14900     // Implements the default empty TriggerField.onTriggerClick function
14901     onTriggerClick : function(e)
14902     {
14903         Roo.log('trigger click');
14904         
14905         if(this.disabled || !this.triggerList){
14906             return;
14907         }
14908         
14909         this.page = 0;
14910         this.loadNext = false;
14911         
14912         if(this.isExpanded()){
14913             this.collapse();
14914             if (!this.blockFocus) {
14915                 this.inputEl().focus();
14916             }
14917             
14918         }else {
14919             this.hasFocus = true;
14920             if(this.triggerAction == 'all') {
14921                 this.doQuery(this.allQuery, true);
14922             } else {
14923                 this.doQuery(this.getRawValue());
14924             }
14925             if (!this.blockFocus) {
14926                 this.inputEl().focus();
14927             }
14928         }
14929     },
14930     
14931     onTickableTriggerClick : function(e)
14932     {
14933         if(this.disabled){
14934             return;
14935         }
14936         
14937         this.page = 0;
14938         this.loadNext = false;
14939         this.hasFocus = true;
14940         
14941         if(this.triggerAction == 'all') {
14942             this.doQuery(this.allQuery, true);
14943         } else {
14944             this.doQuery(this.getRawValue());
14945         }
14946     },
14947     
14948     onSearchFieldClick : function(e)
14949     {
14950         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14951             this.onTickableFooterButtonClick(e, false, false);
14952             return;
14953         }
14954         
14955         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14956             return;
14957         }
14958         
14959         this.page = 0;
14960         this.loadNext = false;
14961         this.hasFocus = true;
14962         
14963         if(this.triggerAction == 'all') {
14964             this.doQuery(this.allQuery, true);
14965         } else {
14966             this.doQuery(this.getRawValue());
14967         }
14968     },
14969     
14970     listKeyPress : function(e)
14971     {
14972         //Roo.log('listkeypress');
14973         // scroll to first matching element based on key pres..
14974         if (e.isSpecialKey()) {
14975             return false;
14976         }
14977         var k = String.fromCharCode(e.getKey()).toUpperCase();
14978         //Roo.log(k);
14979         var match  = false;
14980         var csel = this.view.getSelectedNodes();
14981         var cselitem = false;
14982         if (csel.length) {
14983             var ix = this.view.indexOf(csel[0]);
14984             cselitem  = this.store.getAt(ix);
14985             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14986                 cselitem = false;
14987             }
14988             
14989         }
14990         
14991         this.store.each(function(v) { 
14992             if (cselitem) {
14993                 // start at existing selection.
14994                 if (cselitem.id == v.id) {
14995                     cselitem = false;
14996                 }
14997                 return true;
14998             }
14999                 
15000             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15001                 match = this.store.indexOf(v);
15002                 return false;
15003             }
15004             return true;
15005         }, this);
15006         
15007         if (match === false) {
15008             return true; // no more action?
15009         }
15010         // scroll to?
15011         this.view.select(match);
15012         var sn = Roo.get(this.view.getSelectedNodes()[0]);
15013         sn.scrollIntoView(sn.dom.parentNode, false);
15014     },
15015     
15016     onViewScroll : function(e, t){
15017         
15018         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){
15019             return;
15020         }
15021         
15022         this.hasQuery = true;
15023         
15024         this.loading = this.list.select('.loading', true).first();
15025         
15026         if(this.loading === null){
15027             this.list.createChild({
15028                 tag: 'div',
15029                 cls: 'loading roo-select2-more-results roo-select2-active',
15030                 html: 'Loading more results...'
15031             });
15032             
15033             this.loading = this.list.select('.loading', true).first();
15034             
15035             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15036             
15037             this.loading.hide();
15038         }
15039         
15040         this.loading.show();
15041         
15042         var _combo = this;
15043         
15044         this.page++;
15045         this.loadNext = true;
15046         
15047         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15048         
15049         return;
15050     },
15051     
15052     addItem : function(o)
15053     {   
15054         var dv = ''; // display value
15055         
15056         if (this.displayField) {
15057             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15058         } else {
15059             // this is an error condition!!!
15060             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15061         }
15062         
15063         if(!dv.length){
15064             return;
15065         }
15066         
15067         var choice = this.choices.createChild({
15068             tag: 'li',
15069             cls: 'roo-select2-search-choice',
15070             cn: [
15071                 {
15072                     tag: 'div',
15073                     html: dv
15074                 },
15075                 {
15076                     tag: 'a',
15077                     href: '#',
15078                     cls: 'roo-select2-search-choice-close fa fa-times',
15079                     tabindex: '-1'
15080                 }
15081             ]
15082             
15083         }, this.searchField);
15084         
15085         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15086         
15087         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15088         
15089         this.item.push(o);
15090         
15091         this.lastData = o;
15092         
15093         this.syncValue();
15094         
15095         this.inputEl().dom.value = '';
15096         
15097         this.validate();
15098     },
15099     
15100     onRemoveItem : function(e, _self, o)
15101     {
15102         e.preventDefault();
15103         
15104         this.lastItem = Roo.apply([], this.item);
15105         
15106         var index = this.item.indexOf(o.data) * 1;
15107         
15108         if( index < 0){
15109             Roo.log('not this item?!');
15110             return;
15111         }
15112         
15113         this.item.splice(index, 1);
15114         o.item.remove();
15115         
15116         this.syncValue();
15117         
15118         this.fireEvent('remove', this, e);
15119         
15120         this.validate();
15121         
15122     },
15123     
15124     syncValue : function()
15125     {
15126         if(!this.item.length){
15127             this.clearValue();
15128             return;
15129         }
15130             
15131         var value = [];
15132         var _this = this;
15133         Roo.each(this.item, function(i){
15134             if(_this.valueField){
15135                 value.push(i[_this.valueField]);
15136                 return;
15137             }
15138
15139             value.push(i);
15140         });
15141
15142         this.value = value.join(',');
15143
15144         if(this.hiddenField){
15145             this.hiddenField.dom.value = this.value;
15146         }
15147         
15148         this.store.fireEvent("datachanged", this.store);
15149         
15150         this.validate();
15151     },
15152     
15153     clearItem : function()
15154     {
15155         if(!this.multiple){
15156             return;
15157         }
15158         
15159         this.item = [];
15160         
15161         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15162            c.remove();
15163         });
15164         
15165         this.syncValue();
15166         
15167         this.validate();
15168         
15169         if(this.tickable && !Roo.isTouch){
15170             this.view.refresh();
15171         }
15172     },
15173     
15174     inputEl: function ()
15175     {
15176         if(Roo.isIOS && this.useNativeIOS){
15177             return this.el.select('select.roo-ios-select', true).first();
15178         }
15179         
15180         if(Roo.isTouch && this.mobileTouchView){
15181             return this.el.select('input.form-control',true).first();
15182         }
15183         
15184         if(this.tickable){
15185             return this.searchField;
15186         }
15187         
15188         return this.el.select('input.form-control',true).first();
15189     },
15190     
15191     onTickableFooterButtonClick : function(e, btn, el)
15192     {
15193         e.preventDefault();
15194         
15195         this.lastItem = Roo.apply([], this.item);
15196         
15197         if(btn && btn.name == 'cancel'){
15198             this.tickItems = Roo.apply([], this.item);
15199             this.collapse();
15200             return;
15201         }
15202         
15203         this.clearItem();
15204         
15205         var _this = this;
15206         
15207         Roo.each(this.tickItems, function(o){
15208             _this.addItem(o);
15209         });
15210         
15211         this.collapse();
15212         
15213     },
15214     
15215     validate : function()
15216     {
15217         if(this.getVisibilityEl().hasClass('hidden')){
15218             return true;
15219         }
15220         
15221         var v = this.getRawValue();
15222         
15223         if(this.multiple){
15224             v = this.getValue();
15225         }
15226         
15227         if(this.disabled || this.allowBlank || v.length){
15228             this.markValid();
15229             return true;
15230         }
15231         
15232         this.markInvalid();
15233         return false;
15234     },
15235     
15236     tickableInputEl : function()
15237     {
15238         if(!this.tickable || !this.editable){
15239             return this.inputEl();
15240         }
15241         
15242         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15243     },
15244     
15245     
15246     getAutoCreateTouchView : function()
15247     {
15248         var id = Roo.id();
15249         
15250         var cfg = {
15251             cls: 'form-group' //input-group
15252         };
15253         
15254         var input =  {
15255             tag: 'input',
15256             id : id,
15257             type : this.inputType,
15258             cls : 'form-control x-combo-noedit',
15259             autocomplete: 'new-password',
15260             placeholder : this.placeholder || '',
15261             readonly : true
15262         };
15263         
15264         if (this.name) {
15265             input.name = this.name;
15266         }
15267         
15268         if (this.size) {
15269             input.cls += ' input-' + this.size;
15270         }
15271         
15272         if (this.disabled) {
15273             input.disabled = true;
15274         }
15275         
15276         var inputblock = {
15277             cls : '',
15278             cn : [
15279                 input
15280             ]
15281         };
15282         
15283         if(this.before){
15284             inputblock.cls += ' input-group';
15285             
15286             inputblock.cn.unshift({
15287                 tag :'span',
15288                 cls : 'input-group-addon input-group-prepend input-group-text',
15289                 html : this.before
15290             });
15291         }
15292         
15293         if(this.removable && !this.multiple){
15294             inputblock.cls += ' roo-removable';
15295             
15296             inputblock.cn.push({
15297                 tag: 'button',
15298                 html : 'x',
15299                 cls : 'roo-combo-removable-btn close'
15300             });
15301         }
15302
15303         if(this.hasFeedback && !this.allowBlank){
15304             
15305             inputblock.cls += ' has-feedback';
15306             
15307             inputblock.cn.push({
15308                 tag: 'span',
15309                 cls: 'glyphicon form-control-feedback'
15310             });
15311             
15312         }
15313         
15314         if (this.after) {
15315             
15316             inputblock.cls += (this.before) ? '' : ' input-group';
15317             
15318             inputblock.cn.push({
15319                 tag :'span',
15320                 cls : 'input-group-addon input-group-append input-group-text',
15321                 html : this.after
15322             });
15323         }
15324
15325         
15326         var ibwrap = inputblock;
15327         
15328         if(this.multiple){
15329             ibwrap = {
15330                 tag: 'ul',
15331                 cls: 'roo-select2-choices',
15332                 cn:[
15333                     {
15334                         tag: 'li',
15335                         cls: 'roo-select2-search-field',
15336                         cn: [
15337
15338                             inputblock
15339                         ]
15340                     }
15341                 ]
15342             };
15343         
15344             
15345         }
15346         
15347         var combobox = {
15348             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15349             cn: [
15350                 {
15351                     tag: 'input',
15352                     type : 'hidden',
15353                     cls: 'form-hidden-field'
15354                 },
15355                 ibwrap
15356             ]
15357         };
15358         
15359         if(!this.multiple && this.showToggleBtn){
15360             
15361             var caret = {
15362                         tag: 'span',
15363                         cls: 'caret'
15364             };
15365             
15366             if (this.caret != false) {
15367                 caret = {
15368                      tag: 'i',
15369                      cls: 'fa fa-' + this.caret
15370                 };
15371                 
15372             }
15373             
15374             combobox.cn.push({
15375                 tag :'span',
15376                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15377                 cn : [
15378                     caret,
15379                     {
15380                         tag: 'span',
15381                         cls: 'combobox-clear',
15382                         cn  : [
15383                             {
15384                                 tag : 'i',
15385                                 cls: 'icon-remove'
15386                             }
15387                         ]
15388                     }
15389                 ]
15390
15391             })
15392         }
15393         
15394         if(this.multiple){
15395             combobox.cls += ' roo-select2-container-multi';
15396         }
15397         
15398         var align = this.labelAlign || this.parentLabelAlign();
15399         
15400         if (align ==='left' && this.fieldLabel.length) {
15401
15402             cfg.cn = [
15403                 {
15404                    tag : 'i',
15405                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15406                    tooltip : 'This field is required'
15407                 },
15408                 {
15409                     tag: 'label',
15410                     cls : 'control-label col-form-label',
15411                     html : this.fieldLabel
15412
15413                 },
15414                 {
15415                     cls : '', 
15416                     cn: [
15417                         combobox
15418                     ]
15419                 }
15420             ];
15421             
15422             var labelCfg = cfg.cn[1];
15423             var contentCfg = cfg.cn[2];
15424             
15425
15426             if(this.indicatorpos == 'right'){
15427                 cfg.cn = [
15428                     {
15429                         tag: 'label',
15430                         'for' :  id,
15431                         cls : 'control-label col-form-label',
15432                         cn : [
15433                             {
15434                                 tag : 'span',
15435                                 html : this.fieldLabel
15436                             },
15437                             {
15438                                 tag : 'i',
15439                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15440                                 tooltip : 'This field is required'
15441                             }
15442                         ]
15443                     },
15444                     {
15445                         cls : "",
15446                         cn: [
15447                             combobox
15448                         ]
15449                     }
15450
15451                 ];
15452                 
15453                 labelCfg = cfg.cn[0];
15454                 contentCfg = cfg.cn[1];
15455             }
15456             
15457            
15458             
15459             if(this.labelWidth > 12){
15460                 labelCfg.style = "width: " + this.labelWidth + 'px';
15461             }
15462             
15463             if(this.labelWidth < 13 && this.labelmd == 0){
15464                 this.labelmd = this.labelWidth;
15465             }
15466             
15467             if(this.labellg > 0){
15468                 labelCfg.cls += ' col-lg-' + this.labellg;
15469                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15470             }
15471             
15472             if(this.labelmd > 0){
15473                 labelCfg.cls += ' col-md-' + this.labelmd;
15474                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15475             }
15476             
15477             if(this.labelsm > 0){
15478                 labelCfg.cls += ' col-sm-' + this.labelsm;
15479                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15480             }
15481             
15482             if(this.labelxs > 0){
15483                 labelCfg.cls += ' col-xs-' + this.labelxs;
15484                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15485             }
15486                 
15487                 
15488         } else if ( this.fieldLabel.length) {
15489             cfg.cn = [
15490                 {
15491                    tag : 'i',
15492                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15493                    tooltip : 'This field is required'
15494                 },
15495                 {
15496                     tag: 'label',
15497                     cls : 'control-label',
15498                     html : this.fieldLabel
15499
15500                 },
15501                 {
15502                     cls : '', 
15503                     cn: [
15504                         combobox
15505                     ]
15506                 }
15507             ];
15508             
15509             if(this.indicatorpos == 'right'){
15510                 cfg.cn = [
15511                     {
15512                         tag: 'label',
15513                         cls : 'control-label',
15514                         html : this.fieldLabel,
15515                         cn : [
15516                             {
15517                                tag : 'i',
15518                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15519                                tooltip : 'This field is required'
15520                             }
15521                         ]
15522                     },
15523                     {
15524                         cls : '', 
15525                         cn: [
15526                             combobox
15527                         ]
15528                     }
15529                 ];
15530             }
15531         } else {
15532             cfg.cn = combobox;    
15533         }
15534         
15535         
15536         var settings = this;
15537         
15538         ['xs','sm','md','lg'].map(function(size){
15539             if (settings[size]) {
15540                 cfg.cls += ' col-' + size + '-' + settings[size];
15541             }
15542         });
15543         
15544         return cfg;
15545     },
15546     
15547     initTouchView : function()
15548     {
15549         this.renderTouchView();
15550         
15551         this.touchViewEl.on('scroll', function(){
15552             this.el.dom.scrollTop = 0;
15553         }, this);
15554         
15555         this.originalValue = this.getValue();
15556         
15557         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15558         
15559         this.inputEl().on("click", this.showTouchView, this);
15560         if (this.triggerEl) {
15561             this.triggerEl.on("click", this.showTouchView, this);
15562         }
15563         
15564         
15565         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15566         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15567         
15568         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15569         
15570         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15571         this.store.on('load', this.onTouchViewLoad, this);
15572         this.store.on('loadexception', this.onTouchViewLoadException, this);
15573         
15574         if(this.hiddenName){
15575             
15576             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15577             
15578             this.hiddenField.dom.value =
15579                 this.hiddenValue !== undefined ? this.hiddenValue :
15580                 this.value !== undefined ? this.value : '';
15581         
15582             this.el.dom.removeAttribute('name');
15583             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15584         }
15585         
15586         if(this.multiple){
15587             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15588             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15589         }
15590         
15591         if(this.removable && !this.multiple){
15592             var close = this.closeTriggerEl();
15593             if(close){
15594                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15595                 close.on('click', this.removeBtnClick, this, close);
15596             }
15597         }
15598         /*
15599          * fix the bug in Safari iOS8
15600          */
15601         this.inputEl().on("focus", function(e){
15602             document.activeElement.blur();
15603         }, this);
15604         
15605         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15606         
15607         return;
15608         
15609         
15610     },
15611     
15612     renderTouchView : function()
15613     {
15614         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15615         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15616         
15617         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15618         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15619         
15620         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15621         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15622         this.touchViewBodyEl.setStyle('overflow', 'auto');
15623         
15624         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15625         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15626         
15627         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15628         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15629         
15630     },
15631     
15632     showTouchView : function()
15633     {
15634         if(this.disabled){
15635             return;
15636         }
15637         
15638         this.touchViewHeaderEl.hide();
15639
15640         if(this.modalTitle.length){
15641             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15642             this.touchViewHeaderEl.show();
15643         }
15644
15645         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15646         this.touchViewEl.show();
15647
15648         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15649         
15650         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15651         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15652
15653         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15654
15655         if(this.modalTitle.length){
15656             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15657         }
15658         
15659         this.touchViewBodyEl.setHeight(bodyHeight);
15660
15661         if(this.animate){
15662             var _this = this;
15663             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15664         }else{
15665             this.touchViewEl.addClass('in');
15666         }
15667         
15668         if(this._touchViewMask){
15669             Roo.get(document.body).addClass("x-body-masked");
15670             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15671             this._touchViewMask.setStyle('z-index', 10000);
15672             this._touchViewMask.addClass('show');
15673         }
15674         
15675         this.doTouchViewQuery();
15676         
15677     },
15678     
15679     hideTouchView : function()
15680     {
15681         this.touchViewEl.removeClass('in');
15682
15683         if(this.animate){
15684             var _this = this;
15685             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15686         }else{
15687             this.touchViewEl.setStyle('display', 'none');
15688         }
15689         
15690         if(this._touchViewMask){
15691             this._touchViewMask.removeClass('show');
15692             Roo.get(document.body).removeClass("x-body-masked");
15693         }
15694     },
15695     
15696     setTouchViewValue : function()
15697     {
15698         if(this.multiple){
15699             this.clearItem();
15700         
15701             var _this = this;
15702
15703             Roo.each(this.tickItems, function(o){
15704                 this.addItem(o);
15705             }, this);
15706         }
15707         
15708         this.hideTouchView();
15709     },
15710     
15711     doTouchViewQuery : function()
15712     {
15713         var qe = {
15714             query: '',
15715             forceAll: true,
15716             combo: this,
15717             cancel:false
15718         };
15719         
15720         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15721             return false;
15722         }
15723         
15724         if(!this.alwaysQuery || this.mode == 'local'){
15725             this.onTouchViewLoad();
15726             return;
15727         }
15728         
15729         this.store.load();
15730     },
15731     
15732     onTouchViewBeforeLoad : function(combo,opts)
15733     {
15734         return;
15735     },
15736
15737     // private
15738     onTouchViewLoad : function()
15739     {
15740         if(this.store.getCount() < 1){
15741             this.onTouchViewEmptyResults();
15742             return;
15743         }
15744         
15745         this.clearTouchView();
15746         
15747         var rawValue = this.getRawValue();
15748         
15749         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15750         
15751         this.tickItems = [];
15752         
15753         this.store.data.each(function(d, rowIndex){
15754             var row = this.touchViewListGroup.createChild(template);
15755             
15756             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15757                 row.addClass(d.data.cls);
15758             }
15759             
15760             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15761                 var cfg = {
15762                     data : d.data,
15763                     html : d.data[this.displayField]
15764                 };
15765                 
15766                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15767                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15768                 }
15769             }
15770             row.removeClass('selected');
15771             if(!this.multiple && this.valueField &&
15772                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15773             {
15774                 // radio buttons..
15775                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15776                 row.addClass('selected');
15777             }
15778             
15779             if(this.multiple && this.valueField &&
15780                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15781             {
15782                 
15783                 // checkboxes...
15784                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15785                 this.tickItems.push(d.data);
15786             }
15787             
15788             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15789             
15790         }, this);
15791         
15792         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15793         
15794         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15795
15796         if(this.modalTitle.length){
15797             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15798         }
15799
15800         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15801         
15802         if(this.mobile_restrict_height && listHeight < bodyHeight){
15803             this.touchViewBodyEl.setHeight(listHeight);
15804         }
15805         
15806         var _this = this;
15807         
15808         if(firstChecked && listHeight > bodyHeight){
15809             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15810         }
15811         
15812     },
15813     
15814     onTouchViewLoadException : function()
15815     {
15816         this.hideTouchView();
15817     },
15818     
15819     onTouchViewEmptyResults : function()
15820     {
15821         this.clearTouchView();
15822         
15823         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15824         
15825         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15826         
15827     },
15828     
15829     clearTouchView : function()
15830     {
15831         this.touchViewListGroup.dom.innerHTML = '';
15832     },
15833     
15834     onTouchViewClick : function(e, el, o)
15835     {
15836         e.preventDefault();
15837         
15838         var row = o.row;
15839         var rowIndex = o.rowIndex;
15840         
15841         var r = this.store.getAt(rowIndex);
15842         
15843         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15844             
15845             if(!this.multiple){
15846                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15847                     c.dom.removeAttribute('checked');
15848                 }, this);
15849
15850                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15851
15852                 this.setFromData(r.data);
15853
15854                 var close = this.closeTriggerEl();
15855
15856                 if(close){
15857                     close.show();
15858                 }
15859
15860                 this.hideTouchView();
15861
15862                 this.fireEvent('select', this, r, rowIndex);
15863
15864                 return;
15865             }
15866
15867             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15868                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15869                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15870                 return;
15871             }
15872
15873             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15874             this.addItem(r.data);
15875             this.tickItems.push(r.data);
15876         }
15877     },
15878     
15879     getAutoCreateNativeIOS : function()
15880     {
15881         var cfg = {
15882             cls: 'form-group' //input-group,
15883         };
15884         
15885         var combobox =  {
15886             tag: 'select',
15887             cls : 'roo-ios-select'
15888         };
15889         
15890         if (this.name) {
15891             combobox.name = this.name;
15892         }
15893         
15894         if (this.disabled) {
15895             combobox.disabled = true;
15896         }
15897         
15898         var settings = this;
15899         
15900         ['xs','sm','md','lg'].map(function(size){
15901             if (settings[size]) {
15902                 cfg.cls += ' col-' + size + '-' + settings[size];
15903             }
15904         });
15905         
15906         cfg.cn = combobox;
15907         
15908         return cfg;
15909         
15910     },
15911     
15912     initIOSView : function()
15913     {
15914         this.store.on('load', this.onIOSViewLoad, this);
15915         
15916         return;
15917     },
15918     
15919     onIOSViewLoad : function()
15920     {
15921         if(this.store.getCount() < 1){
15922             return;
15923         }
15924         
15925         this.clearIOSView();
15926         
15927         if(this.allowBlank) {
15928             
15929             var default_text = '-- SELECT --';
15930             
15931             if(this.placeholder.length){
15932                 default_text = this.placeholder;
15933             }
15934             
15935             if(this.emptyTitle.length){
15936                 default_text += ' - ' + this.emptyTitle + ' -';
15937             }
15938             
15939             var opt = this.inputEl().createChild({
15940                 tag: 'option',
15941                 value : 0,
15942                 html : default_text
15943             });
15944             
15945             var o = {};
15946             o[this.valueField] = 0;
15947             o[this.displayField] = default_text;
15948             
15949             this.ios_options.push({
15950                 data : o,
15951                 el : opt
15952             });
15953             
15954         }
15955         
15956         this.store.data.each(function(d, rowIndex){
15957             
15958             var html = '';
15959             
15960             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15961                 html = d.data[this.displayField];
15962             }
15963             
15964             var value = '';
15965             
15966             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15967                 value = d.data[this.valueField];
15968             }
15969             
15970             var option = {
15971                 tag: 'option',
15972                 value : value,
15973                 html : html
15974             };
15975             
15976             if(this.value == d.data[this.valueField]){
15977                 option['selected'] = true;
15978             }
15979             
15980             var opt = this.inputEl().createChild(option);
15981             
15982             this.ios_options.push({
15983                 data : d.data,
15984                 el : opt
15985             });
15986             
15987         }, this);
15988         
15989         this.inputEl().on('change', function(){
15990            this.fireEvent('select', this);
15991         }, this);
15992         
15993     },
15994     
15995     clearIOSView: function()
15996     {
15997         this.inputEl().dom.innerHTML = '';
15998         
15999         this.ios_options = [];
16000     },
16001     
16002     setIOSValue: function(v)
16003     {
16004         this.value = v;
16005         
16006         if(!this.ios_options){
16007             return;
16008         }
16009         
16010         Roo.each(this.ios_options, function(opts){
16011            
16012            opts.el.dom.removeAttribute('selected');
16013            
16014            if(opts.data[this.valueField] != v){
16015                return;
16016            }
16017            
16018            opts.el.dom.setAttribute('selected', true);
16019            
16020         }, this);
16021     }
16022
16023     /** 
16024     * @cfg {Boolean} grow 
16025     * @hide 
16026     */
16027     /** 
16028     * @cfg {Number} growMin 
16029     * @hide 
16030     */
16031     /** 
16032     * @cfg {Number} growMax 
16033     * @hide 
16034     */
16035     /**
16036      * @hide
16037      * @method autoSize
16038      */
16039 });
16040
16041 Roo.apply(Roo.bootstrap.ComboBox,  {
16042     
16043     header : {
16044         tag: 'div',
16045         cls: 'modal-header',
16046         cn: [
16047             {
16048                 tag: 'h4',
16049                 cls: 'modal-title'
16050             }
16051         ]
16052     },
16053     
16054     body : {
16055         tag: 'div',
16056         cls: 'modal-body',
16057         cn: [
16058             {
16059                 tag: 'ul',
16060                 cls: 'list-group'
16061             }
16062         ]
16063     },
16064     
16065     listItemRadio : {
16066         tag: 'li',
16067         cls: 'list-group-item',
16068         cn: [
16069             {
16070                 tag: 'span',
16071                 cls: 'roo-combobox-list-group-item-value'
16072             },
16073             {
16074                 tag: 'div',
16075                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16076                 cn: [
16077                     {
16078                         tag: 'input',
16079                         type: 'radio'
16080                     },
16081                     {
16082                         tag: 'label'
16083                     }
16084                 ]
16085             }
16086         ]
16087     },
16088     
16089     listItemCheckbox : {
16090         tag: 'li',
16091         cls: 'list-group-item',
16092         cn: [
16093             {
16094                 tag: 'span',
16095                 cls: 'roo-combobox-list-group-item-value'
16096             },
16097             {
16098                 tag: 'div',
16099                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16100                 cn: [
16101                     {
16102                         tag: 'input',
16103                         type: 'checkbox'
16104                     },
16105                     {
16106                         tag: 'label'
16107                     }
16108                 ]
16109             }
16110         ]
16111     },
16112     
16113     emptyResult : {
16114         tag: 'div',
16115         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16116     },
16117     
16118     footer : {
16119         tag: 'div',
16120         cls: 'modal-footer',
16121         cn: [
16122             {
16123                 tag: 'div',
16124                 cls: 'row',
16125                 cn: [
16126                     {
16127                         tag: 'div',
16128                         cls: 'col-xs-6 text-left',
16129                         cn: {
16130                             tag: 'button',
16131                             cls: 'btn btn-danger roo-touch-view-cancel',
16132                             html: 'Cancel'
16133                         }
16134                     },
16135                     {
16136                         tag: 'div',
16137                         cls: 'col-xs-6 text-right',
16138                         cn: {
16139                             tag: 'button',
16140                             cls: 'btn btn-success roo-touch-view-ok',
16141                             html: 'OK'
16142                         }
16143                     }
16144                 ]
16145             }
16146         ]
16147         
16148     }
16149 });
16150
16151 Roo.apply(Roo.bootstrap.ComboBox,  {
16152     
16153     touchViewTemplate : {
16154         tag: 'div',
16155         cls: 'modal fade roo-combobox-touch-view',
16156         cn: [
16157             {
16158                 tag: 'div',
16159                 cls: 'modal-dialog',
16160                 style : 'position:fixed', // we have to fix position....
16161                 cn: [
16162                     {
16163                         tag: 'div',
16164                         cls: 'modal-content',
16165                         cn: [
16166                             Roo.bootstrap.ComboBox.header,
16167                             Roo.bootstrap.ComboBox.body,
16168                             Roo.bootstrap.ComboBox.footer
16169                         ]
16170                     }
16171                 ]
16172             }
16173         ]
16174     }
16175 });/*
16176  * Based on:
16177  * Ext JS Library 1.1.1
16178  * Copyright(c) 2006-2007, Ext JS, LLC.
16179  *
16180  * Originally Released Under LGPL - original licence link has changed is not relivant.
16181  *
16182  * Fork - LGPL
16183  * <script type="text/javascript">
16184  */
16185
16186 /**
16187  * @class Roo.View
16188  * @extends Roo.util.Observable
16189  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16190  * This class also supports single and multi selection modes. <br>
16191  * Create a data model bound view:
16192  <pre><code>
16193  var store = new Roo.data.Store(...);
16194
16195  var view = new Roo.View({
16196     el : "my-element",
16197     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16198  
16199     singleSelect: true,
16200     selectedClass: "ydataview-selected",
16201     store: store
16202  });
16203
16204  // listen for node click?
16205  view.on("click", function(vw, index, node, e){
16206  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16207  });
16208
16209  // load XML data
16210  dataModel.load("foobar.xml");
16211  </code></pre>
16212  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16213  * <br><br>
16214  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16215  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16216  * 
16217  * Note: old style constructor is still suported (container, template, config)
16218  * 
16219  * @constructor
16220  * Create a new View
16221  * @param {Object} config The config object
16222  * 
16223  */
16224 Roo.View = function(config, depreciated_tpl, depreciated_config){
16225     
16226     this.parent = false;
16227     
16228     if (typeof(depreciated_tpl) == 'undefined') {
16229         // new way.. - universal constructor.
16230         Roo.apply(this, config);
16231         this.el  = Roo.get(this.el);
16232     } else {
16233         // old format..
16234         this.el  = Roo.get(config);
16235         this.tpl = depreciated_tpl;
16236         Roo.apply(this, depreciated_config);
16237     }
16238     this.wrapEl  = this.el.wrap().wrap();
16239     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16240     
16241     
16242     if(typeof(this.tpl) == "string"){
16243         this.tpl = new Roo.Template(this.tpl);
16244     } else {
16245         // support xtype ctors..
16246         this.tpl = new Roo.factory(this.tpl, Roo);
16247     }
16248     
16249     
16250     this.tpl.compile();
16251     
16252     /** @private */
16253     this.addEvents({
16254         /**
16255          * @event beforeclick
16256          * Fires before a click is processed. Returns false to cancel the default action.
16257          * @param {Roo.View} this
16258          * @param {Number} index The index of the target node
16259          * @param {HTMLElement} node The target node
16260          * @param {Roo.EventObject} e The raw event object
16261          */
16262             "beforeclick" : true,
16263         /**
16264          * @event click
16265          * Fires when a template node is clicked.
16266          * @param {Roo.View} this
16267          * @param {Number} index The index of the target node
16268          * @param {HTMLElement} node The target node
16269          * @param {Roo.EventObject} e The raw event object
16270          */
16271             "click" : true,
16272         /**
16273          * @event dblclick
16274          * Fires when a template node is double clicked.
16275          * @param {Roo.View} this
16276          * @param {Number} index The index of the target node
16277          * @param {HTMLElement} node The target node
16278          * @param {Roo.EventObject} e The raw event object
16279          */
16280             "dblclick" : true,
16281         /**
16282          * @event contextmenu
16283          * Fires when a template node is right clicked.
16284          * @param {Roo.View} this
16285          * @param {Number} index The index of the target node
16286          * @param {HTMLElement} node The target node
16287          * @param {Roo.EventObject} e The raw event object
16288          */
16289             "contextmenu" : true,
16290         /**
16291          * @event selectionchange
16292          * Fires when the selected nodes change.
16293          * @param {Roo.View} this
16294          * @param {Array} selections Array of the selected nodes
16295          */
16296             "selectionchange" : true,
16297     
16298         /**
16299          * @event beforeselect
16300          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16301          * @param {Roo.View} this
16302          * @param {HTMLElement} node The node to be selected
16303          * @param {Array} selections Array of currently selected nodes
16304          */
16305             "beforeselect" : true,
16306         /**
16307          * @event preparedata
16308          * Fires on every row to render, to allow you to change the data.
16309          * @param {Roo.View} this
16310          * @param {Object} data to be rendered (change this)
16311          */
16312           "preparedata" : true
16313           
16314           
16315         });
16316
16317
16318
16319     this.el.on({
16320         "click": this.onClick,
16321         "dblclick": this.onDblClick,
16322         "contextmenu": this.onContextMenu,
16323         scope:this
16324     });
16325
16326     this.selections = [];
16327     this.nodes = [];
16328     this.cmp = new Roo.CompositeElementLite([]);
16329     if(this.store){
16330         this.store = Roo.factory(this.store, Roo.data);
16331         this.setStore(this.store, true);
16332     }
16333     
16334     if ( this.footer && this.footer.xtype) {
16335            
16336          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16337         
16338         this.footer.dataSource = this.store;
16339         this.footer.container = fctr;
16340         this.footer = Roo.factory(this.footer, Roo);
16341         fctr.insertFirst(this.el);
16342         
16343         // this is a bit insane - as the paging toolbar seems to detach the el..
16344 //        dom.parentNode.parentNode.parentNode
16345          // they get detached?
16346     }
16347     
16348     
16349     Roo.View.superclass.constructor.call(this);
16350     
16351     
16352 };
16353
16354 Roo.extend(Roo.View, Roo.util.Observable, {
16355     
16356      /**
16357      * @cfg {Roo.data.Store} store Data store to load data from.
16358      */
16359     store : false,
16360     
16361     /**
16362      * @cfg {String|Roo.Element} el The container element.
16363      */
16364     el : '',
16365     
16366     /**
16367      * @cfg {String|Roo.Template} tpl The template used by this View 
16368      */
16369     tpl : false,
16370     /**
16371      * @cfg {String} dataName the named area of the template to use as the data area
16372      *                          Works with domtemplates roo-name="name"
16373      */
16374     dataName: false,
16375     /**
16376      * @cfg {String} selectedClass The css class to add to selected nodes
16377      */
16378     selectedClass : "x-view-selected",
16379      /**
16380      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16381      */
16382     emptyText : "",
16383     
16384     /**
16385      * @cfg {String} text to display on mask (default Loading)
16386      */
16387     mask : false,
16388     /**
16389      * @cfg {Boolean} multiSelect Allow multiple selection
16390      */
16391     multiSelect : false,
16392     /**
16393      * @cfg {Boolean} singleSelect Allow single selection
16394      */
16395     singleSelect:  false,
16396     
16397     /**
16398      * @cfg {Boolean} toggleSelect - selecting 
16399      */
16400     toggleSelect : false,
16401     
16402     /**
16403      * @cfg {Boolean} tickable - selecting 
16404      */
16405     tickable : false,
16406     
16407     /**
16408      * Returns the element this view is bound to.
16409      * @return {Roo.Element}
16410      */
16411     getEl : function(){
16412         return this.wrapEl;
16413     },
16414     
16415     
16416
16417     /**
16418      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16419      */
16420     refresh : function(){
16421         //Roo.log('refresh');
16422         var t = this.tpl;
16423         
16424         // if we are using something like 'domtemplate', then
16425         // the what gets used is:
16426         // t.applySubtemplate(NAME, data, wrapping data..)
16427         // the outer template then get' applied with
16428         //     the store 'extra data'
16429         // and the body get's added to the
16430         //      roo-name="data" node?
16431         //      <span class='roo-tpl-{name}'></span> ?????
16432         
16433         
16434         
16435         this.clearSelections();
16436         this.el.update("");
16437         var html = [];
16438         var records = this.store.getRange();
16439         if(records.length < 1) {
16440             
16441             // is this valid??  = should it render a template??
16442             
16443             this.el.update(this.emptyText);
16444             return;
16445         }
16446         var el = this.el;
16447         if (this.dataName) {
16448             this.el.update(t.apply(this.store.meta)); //????
16449             el = this.el.child('.roo-tpl-' + this.dataName);
16450         }
16451         
16452         for(var i = 0, len = records.length; i < len; i++){
16453             var data = this.prepareData(records[i].data, i, records[i]);
16454             this.fireEvent("preparedata", this, data, i, records[i]);
16455             
16456             var d = Roo.apply({}, data);
16457             
16458             if(this.tickable){
16459                 Roo.apply(d, {'roo-id' : Roo.id()});
16460                 
16461                 var _this = this;
16462             
16463                 Roo.each(this.parent.item, function(item){
16464                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16465                         return;
16466                     }
16467                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16468                 });
16469             }
16470             
16471             html[html.length] = Roo.util.Format.trim(
16472                 this.dataName ?
16473                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16474                     t.apply(d)
16475             );
16476         }
16477         
16478         
16479         
16480         el.update(html.join(""));
16481         this.nodes = el.dom.childNodes;
16482         this.updateIndexes(0);
16483     },
16484     
16485
16486     /**
16487      * Function to override to reformat the data that is sent to
16488      * the template for each node.
16489      * DEPRICATED - use the preparedata event handler.
16490      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16491      * a JSON object for an UpdateManager bound view).
16492      */
16493     prepareData : function(data, index, record)
16494     {
16495         this.fireEvent("preparedata", this, data, index, record);
16496         return data;
16497     },
16498
16499     onUpdate : function(ds, record){
16500         // Roo.log('on update');   
16501         this.clearSelections();
16502         var index = this.store.indexOf(record);
16503         var n = this.nodes[index];
16504         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16505         n.parentNode.removeChild(n);
16506         this.updateIndexes(index, index);
16507     },
16508
16509     
16510     
16511 // --------- FIXME     
16512     onAdd : function(ds, records, index)
16513     {
16514         //Roo.log(['on Add', ds, records, index] );        
16515         this.clearSelections();
16516         if(this.nodes.length == 0){
16517             this.refresh();
16518             return;
16519         }
16520         var n = this.nodes[index];
16521         for(var i = 0, len = records.length; i < len; i++){
16522             var d = this.prepareData(records[i].data, i, records[i]);
16523             if(n){
16524                 this.tpl.insertBefore(n, d);
16525             }else{
16526                 
16527                 this.tpl.append(this.el, d);
16528             }
16529         }
16530         this.updateIndexes(index);
16531     },
16532
16533     onRemove : function(ds, record, index){
16534        // Roo.log('onRemove');
16535         this.clearSelections();
16536         var el = this.dataName  ?
16537             this.el.child('.roo-tpl-' + this.dataName) :
16538             this.el; 
16539         
16540         el.dom.removeChild(this.nodes[index]);
16541         this.updateIndexes(index);
16542     },
16543
16544     /**
16545      * Refresh an individual node.
16546      * @param {Number} index
16547      */
16548     refreshNode : function(index){
16549         this.onUpdate(this.store, this.store.getAt(index));
16550     },
16551
16552     updateIndexes : function(startIndex, endIndex){
16553         var ns = this.nodes;
16554         startIndex = startIndex || 0;
16555         endIndex = endIndex || ns.length - 1;
16556         for(var i = startIndex; i <= endIndex; i++){
16557             ns[i].nodeIndex = i;
16558         }
16559     },
16560
16561     /**
16562      * Changes the data store this view uses and refresh the view.
16563      * @param {Store} store
16564      */
16565     setStore : function(store, initial){
16566         if(!initial && this.store){
16567             this.store.un("datachanged", this.refresh);
16568             this.store.un("add", this.onAdd);
16569             this.store.un("remove", this.onRemove);
16570             this.store.un("update", this.onUpdate);
16571             this.store.un("clear", this.refresh);
16572             this.store.un("beforeload", this.onBeforeLoad);
16573             this.store.un("load", this.onLoad);
16574             this.store.un("loadexception", this.onLoad);
16575         }
16576         if(store){
16577           
16578             store.on("datachanged", this.refresh, this);
16579             store.on("add", this.onAdd, this);
16580             store.on("remove", this.onRemove, this);
16581             store.on("update", this.onUpdate, this);
16582             store.on("clear", this.refresh, this);
16583             store.on("beforeload", this.onBeforeLoad, this);
16584             store.on("load", this.onLoad, this);
16585             store.on("loadexception", this.onLoad, this);
16586         }
16587         
16588         if(store){
16589             this.refresh();
16590         }
16591     },
16592     /**
16593      * onbeforeLoad - masks the loading area.
16594      *
16595      */
16596     onBeforeLoad : function(store,opts)
16597     {
16598          //Roo.log('onBeforeLoad');   
16599         if (!opts.add) {
16600             this.el.update("");
16601         }
16602         this.el.mask(this.mask ? this.mask : "Loading" ); 
16603     },
16604     onLoad : function ()
16605     {
16606         this.el.unmask();
16607     },
16608     
16609
16610     /**
16611      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16612      * @param {HTMLElement} node
16613      * @return {HTMLElement} The template node
16614      */
16615     findItemFromChild : function(node){
16616         var el = this.dataName  ?
16617             this.el.child('.roo-tpl-' + this.dataName,true) :
16618             this.el.dom; 
16619         
16620         if(!node || node.parentNode == el){
16621                     return node;
16622             }
16623             var p = node.parentNode;
16624             while(p && p != el){
16625             if(p.parentNode == el){
16626                 return p;
16627             }
16628             p = p.parentNode;
16629         }
16630             return null;
16631     },
16632
16633     /** @ignore */
16634     onClick : function(e){
16635         var item = this.findItemFromChild(e.getTarget());
16636         if(item){
16637             var index = this.indexOf(item);
16638             if(this.onItemClick(item, index, e) !== false){
16639                 this.fireEvent("click", this, index, item, e);
16640             }
16641         }else{
16642             this.clearSelections();
16643         }
16644     },
16645
16646     /** @ignore */
16647     onContextMenu : function(e){
16648         var item = this.findItemFromChild(e.getTarget());
16649         if(item){
16650             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16651         }
16652     },
16653
16654     /** @ignore */
16655     onDblClick : function(e){
16656         var item = this.findItemFromChild(e.getTarget());
16657         if(item){
16658             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16659         }
16660     },
16661
16662     onItemClick : function(item, index, e)
16663     {
16664         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16665             return false;
16666         }
16667         if (this.toggleSelect) {
16668             var m = this.isSelected(item) ? 'unselect' : 'select';
16669             //Roo.log(m);
16670             var _t = this;
16671             _t[m](item, true, false);
16672             return true;
16673         }
16674         if(this.multiSelect || this.singleSelect){
16675             if(this.multiSelect && e.shiftKey && this.lastSelection){
16676                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16677             }else{
16678                 this.select(item, this.multiSelect && e.ctrlKey);
16679                 this.lastSelection = item;
16680             }
16681             
16682             if(!this.tickable){
16683                 e.preventDefault();
16684             }
16685             
16686         }
16687         return true;
16688     },
16689
16690     /**
16691      * Get the number of selected nodes.
16692      * @return {Number}
16693      */
16694     getSelectionCount : function(){
16695         return this.selections.length;
16696     },
16697
16698     /**
16699      * Get the currently selected nodes.
16700      * @return {Array} An array of HTMLElements
16701      */
16702     getSelectedNodes : function(){
16703         return this.selections;
16704     },
16705
16706     /**
16707      * Get the indexes of the selected nodes.
16708      * @return {Array}
16709      */
16710     getSelectedIndexes : function(){
16711         var indexes = [], s = this.selections;
16712         for(var i = 0, len = s.length; i < len; i++){
16713             indexes.push(s[i].nodeIndex);
16714         }
16715         return indexes;
16716     },
16717
16718     /**
16719      * Clear all selections
16720      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16721      */
16722     clearSelections : function(suppressEvent){
16723         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16724             this.cmp.elements = this.selections;
16725             this.cmp.removeClass(this.selectedClass);
16726             this.selections = [];
16727             if(!suppressEvent){
16728                 this.fireEvent("selectionchange", this, this.selections);
16729             }
16730         }
16731     },
16732
16733     /**
16734      * Returns true if the passed node is selected
16735      * @param {HTMLElement/Number} node The node or node index
16736      * @return {Boolean}
16737      */
16738     isSelected : function(node){
16739         var s = this.selections;
16740         if(s.length < 1){
16741             return false;
16742         }
16743         node = this.getNode(node);
16744         return s.indexOf(node) !== -1;
16745     },
16746
16747     /**
16748      * Selects nodes.
16749      * @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
16750      * @param {Boolean} keepExisting (optional) true to keep existing selections
16751      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16752      */
16753     select : function(nodeInfo, keepExisting, suppressEvent){
16754         if(nodeInfo instanceof Array){
16755             if(!keepExisting){
16756                 this.clearSelections(true);
16757             }
16758             for(var i = 0, len = nodeInfo.length; i < len; i++){
16759                 this.select(nodeInfo[i], true, true);
16760             }
16761             return;
16762         } 
16763         var node = this.getNode(nodeInfo);
16764         if(!node || this.isSelected(node)){
16765             return; // already selected.
16766         }
16767         if(!keepExisting){
16768             this.clearSelections(true);
16769         }
16770         
16771         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16772             Roo.fly(node).addClass(this.selectedClass);
16773             this.selections.push(node);
16774             if(!suppressEvent){
16775                 this.fireEvent("selectionchange", this, this.selections);
16776             }
16777         }
16778         
16779         
16780     },
16781       /**
16782      * Unselects nodes.
16783      * @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
16784      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16785      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16786      */
16787     unselect : function(nodeInfo, keepExisting, suppressEvent)
16788     {
16789         if(nodeInfo instanceof Array){
16790             Roo.each(this.selections, function(s) {
16791                 this.unselect(s, nodeInfo);
16792             }, this);
16793             return;
16794         }
16795         var node = this.getNode(nodeInfo);
16796         if(!node || !this.isSelected(node)){
16797             //Roo.log("not selected");
16798             return; // not selected.
16799         }
16800         // fireevent???
16801         var ns = [];
16802         Roo.each(this.selections, function(s) {
16803             if (s == node ) {
16804                 Roo.fly(node).removeClass(this.selectedClass);
16805
16806                 return;
16807             }
16808             ns.push(s);
16809         },this);
16810         
16811         this.selections= ns;
16812         this.fireEvent("selectionchange", this, this.selections);
16813     },
16814
16815     /**
16816      * Gets a template node.
16817      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16818      * @return {HTMLElement} The node or null if it wasn't found
16819      */
16820     getNode : function(nodeInfo){
16821         if(typeof nodeInfo == "string"){
16822             return document.getElementById(nodeInfo);
16823         }else if(typeof nodeInfo == "number"){
16824             return this.nodes[nodeInfo];
16825         }
16826         return nodeInfo;
16827     },
16828
16829     /**
16830      * Gets a range template nodes.
16831      * @param {Number} startIndex
16832      * @param {Number} endIndex
16833      * @return {Array} An array of nodes
16834      */
16835     getNodes : function(start, end){
16836         var ns = this.nodes;
16837         start = start || 0;
16838         end = typeof end == "undefined" ? ns.length - 1 : end;
16839         var nodes = [];
16840         if(start <= end){
16841             for(var i = start; i <= end; i++){
16842                 nodes.push(ns[i]);
16843             }
16844         } else{
16845             for(var i = start; i >= end; i--){
16846                 nodes.push(ns[i]);
16847             }
16848         }
16849         return nodes;
16850     },
16851
16852     /**
16853      * Finds the index of the passed node
16854      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16855      * @return {Number} The index of the node or -1
16856      */
16857     indexOf : function(node){
16858         node = this.getNode(node);
16859         if(typeof node.nodeIndex == "number"){
16860             return node.nodeIndex;
16861         }
16862         var ns = this.nodes;
16863         for(var i = 0, len = ns.length; i < len; i++){
16864             if(ns[i] == node){
16865                 return i;
16866             }
16867         }
16868         return -1;
16869     }
16870 });
16871 /*
16872  * - LGPL
16873  *
16874  * based on jquery fullcalendar
16875  * 
16876  */
16877
16878 Roo.bootstrap = Roo.bootstrap || {};
16879 /**
16880  * @class Roo.bootstrap.Calendar
16881  * @extends Roo.bootstrap.Component
16882  * Bootstrap Calendar class
16883  * @cfg {Boolean} loadMask (true|false) default false
16884  * @cfg {Object} header generate the user specific header of the calendar, default false
16885
16886  * @constructor
16887  * Create a new Container
16888  * @param {Object} config The config object
16889  */
16890
16891
16892
16893 Roo.bootstrap.Calendar = function(config){
16894     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16895      this.addEvents({
16896         /**
16897              * @event select
16898              * Fires when a date is selected
16899              * @param {DatePicker} this
16900              * @param {Date} date The selected date
16901              */
16902         'select': true,
16903         /**
16904              * @event monthchange
16905              * Fires when the displayed month changes 
16906              * @param {DatePicker} this
16907              * @param {Date} date The selected month
16908              */
16909         'monthchange': true,
16910         /**
16911              * @event evententer
16912              * Fires when mouse over an event
16913              * @param {Calendar} this
16914              * @param {event} Event
16915              */
16916         'evententer': true,
16917         /**
16918              * @event eventleave
16919              * Fires when the mouse leaves an
16920              * @param {Calendar} this
16921              * @param {event}
16922              */
16923         'eventleave': true,
16924         /**
16925              * @event eventclick
16926              * Fires when the mouse click an
16927              * @param {Calendar} this
16928              * @param {event}
16929              */
16930         'eventclick': true
16931         
16932     });
16933
16934 };
16935
16936 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16937     
16938      /**
16939      * @cfg {Number} startDay
16940      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16941      */
16942     startDay : 0,
16943     
16944     loadMask : false,
16945     
16946     header : false,
16947       
16948     getAutoCreate : function(){
16949         
16950         
16951         var fc_button = function(name, corner, style, content ) {
16952             return Roo.apply({},{
16953                 tag : 'span',
16954                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16955                          (corner.length ?
16956                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16957                             ''
16958                         ),
16959                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16960                 unselectable: 'on'
16961             });
16962         };
16963         
16964         var header = {};
16965         
16966         if(!this.header){
16967             header = {
16968                 tag : 'table',
16969                 cls : 'fc-header',
16970                 style : 'width:100%',
16971                 cn : [
16972                     {
16973                         tag: 'tr',
16974                         cn : [
16975                             {
16976                                 tag : 'td',
16977                                 cls : 'fc-header-left',
16978                                 cn : [
16979                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16980                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16981                                     { tag: 'span', cls: 'fc-header-space' },
16982                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16983
16984
16985                                 ]
16986                             },
16987
16988                             {
16989                                 tag : 'td',
16990                                 cls : 'fc-header-center',
16991                                 cn : [
16992                                     {
16993                                         tag: 'span',
16994                                         cls: 'fc-header-title',
16995                                         cn : {
16996                                             tag: 'H2',
16997                                             html : 'month / year'
16998                                         }
16999                                     }
17000
17001                                 ]
17002                             },
17003                             {
17004                                 tag : 'td',
17005                                 cls : 'fc-header-right',
17006                                 cn : [
17007                               /*      fc_button('month', 'left', '', 'month' ),
17008                                     fc_button('week', '', '', 'week' ),
17009                                     fc_button('day', 'right', '', 'day' )
17010                                 */    
17011
17012                                 ]
17013                             }
17014
17015                         ]
17016                     }
17017                 ]
17018             };
17019         }
17020         
17021         header = this.header;
17022         
17023        
17024         var cal_heads = function() {
17025             var ret = [];
17026             // fixme - handle this.
17027             
17028             for (var i =0; i < Date.dayNames.length; i++) {
17029                 var d = Date.dayNames[i];
17030                 ret.push({
17031                     tag: 'th',
17032                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17033                     html : d.substring(0,3)
17034                 });
17035                 
17036             }
17037             ret[0].cls += ' fc-first';
17038             ret[6].cls += ' fc-last';
17039             return ret;
17040         };
17041         var cal_cell = function(n) {
17042             return  {
17043                 tag: 'td',
17044                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17045                 cn : [
17046                     {
17047                         cn : [
17048                             {
17049                                 cls: 'fc-day-number',
17050                                 html: 'D'
17051                             },
17052                             {
17053                                 cls: 'fc-day-content',
17054                              
17055                                 cn : [
17056                                      {
17057                                         style: 'position: relative;' // height: 17px;
17058                                     }
17059                                 ]
17060                             }
17061                             
17062                             
17063                         ]
17064                     }
17065                 ]
17066                 
17067             }
17068         };
17069         var cal_rows = function() {
17070             
17071             var ret = [];
17072             for (var r = 0; r < 6; r++) {
17073                 var row= {
17074                     tag : 'tr',
17075                     cls : 'fc-week',
17076                     cn : []
17077                 };
17078                 
17079                 for (var i =0; i < Date.dayNames.length; i++) {
17080                     var d = Date.dayNames[i];
17081                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17082
17083                 }
17084                 row.cn[0].cls+=' fc-first';
17085                 row.cn[0].cn[0].style = 'min-height:90px';
17086                 row.cn[6].cls+=' fc-last';
17087                 ret.push(row);
17088                 
17089             }
17090             ret[0].cls += ' fc-first';
17091             ret[4].cls += ' fc-prev-last';
17092             ret[5].cls += ' fc-last';
17093             return ret;
17094             
17095         };
17096         
17097         var cal_table = {
17098             tag: 'table',
17099             cls: 'fc-border-separate',
17100             style : 'width:100%',
17101             cellspacing  : 0,
17102             cn : [
17103                 { 
17104                     tag: 'thead',
17105                     cn : [
17106                         { 
17107                             tag: 'tr',
17108                             cls : 'fc-first fc-last',
17109                             cn : cal_heads()
17110                         }
17111                     ]
17112                 },
17113                 { 
17114                     tag: 'tbody',
17115                     cn : cal_rows()
17116                 }
17117                   
17118             ]
17119         };
17120          
17121          var cfg = {
17122             cls : 'fc fc-ltr',
17123             cn : [
17124                 header,
17125                 {
17126                     cls : 'fc-content',
17127                     style : "position: relative;",
17128                     cn : [
17129                         {
17130                             cls : 'fc-view fc-view-month fc-grid',
17131                             style : 'position: relative',
17132                             unselectable : 'on',
17133                             cn : [
17134                                 {
17135                                     cls : 'fc-event-container',
17136                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17137                                 },
17138                                 cal_table
17139                             ]
17140                         }
17141                     ]
17142     
17143                 }
17144            ] 
17145             
17146         };
17147         
17148          
17149         
17150         return cfg;
17151     },
17152     
17153     
17154     initEvents : function()
17155     {
17156         if(!this.store){
17157             throw "can not find store for calendar";
17158         }
17159         
17160         var mark = {
17161             tag: "div",
17162             cls:"x-dlg-mask",
17163             style: "text-align:center",
17164             cn: [
17165                 {
17166                     tag: "div",
17167                     style: "background-color:white;width:50%;margin:250 auto",
17168                     cn: [
17169                         {
17170                             tag: "img",
17171                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17172                         },
17173                         {
17174                             tag: "span",
17175                             html: "Loading"
17176                         }
17177                         
17178                     ]
17179                 }
17180             ]
17181         };
17182         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17183         
17184         var size = this.el.select('.fc-content', true).first().getSize();
17185         this.maskEl.setSize(size.width, size.height);
17186         this.maskEl.enableDisplayMode("block");
17187         if(!this.loadMask){
17188             this.maskEl.hide();
17189         }
17190         
17191         this.store = Roo.factory(this.store, Roo.data);
17192         this.store.on('load', this.onLoad, this);
17193         this.store.on('beforeload', this.onBeforeLoad, this);
17194         
17195         this.resize();
17196         
17197         this.cells = this.el.select('.fc-day',true);
17198         //Roo.log(this.cells);
17199         this.textNodes = this.el.query('.fc-day-number');
17200         this.cells.addClassOnOver('fc-state-hover');
17201         
17202         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17203         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17204         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17205         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17206         
17207         this.on('monthchange', this.onMonthChange, this);
17208         
17209         this.update(new Date().clearTime());
17210     },
17211     
17212     resize : function() {
17213         var sz  = this.el.getSize();
17214         
17215         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17216         this.el.select('.fc-day-content div',true).setHeight(34);
17217     },
17218     
17219     
17220     // private
17221     showPrevMonth : function(e){
17222         this.update(this.activeDate.add("mo", -1));
17223     },
17224     showToday : function(e){
17225         this.update(new Date().clearTime());
17226     },
17227     // private
17228     showNextMonth : function(e){
17229         this.update(this.activeDate.add("mo", 1));
17230     },
17231
17232     // private
17233     showPrevYear : function(){
17234         this.update(this.activeDate.add("y", -1));
17235     },
17236
17237     // private
17238     showNextYear : function(){
17239         this.update(this.activeDate.add("y", 1));
17240     },
17241
17242     
17243    // private
17244     update : function(date)
17245     {
17246         var vd = this.activeDate;
17247         this.activeDate = date;
17248 //        if(vd && this.el){
17249 //            var t = date.getTime();
17250 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17251 //                Roo.log('using add remove');
17252 //                
17253 //                this.fireEvent('monthchange', this, date);
17254 //                
17255 //                this.cells.removeClass("fc-state-highlight");
17256 //                this.cells.each(function(c){
17257 //                   if(c.dateValue == t){
17258 //                       c.addClass("fc-state-highlight");
17259 //                       setTimeout(function(){
17260 //                            try{c.dom.firstChild.focus();}catch(e){}
17261 //                       }, 50);
17262 //                       return false;
17263 //                   }
17264 //                   return true;
17265 //                });
17266 //                return;
17267 //            }
17268 //        }
17269         
17270         var days = date.getDaysInMonth();
17271         
17272         var firstOfMonth = date.getFirstDateOfMonth();
17273         var startingPos = firstOfMonth.getDay()-this.startDay;
17274         
17275         if(startingPos < this.startDay){
17276             startingPos += 7;
17277         }
17278         
17279         var pm = date.add(Date.MONTH, -1);
17280         var prevStart = pm.getDaysInMonth()-startingPos;
17281 //        
17282         this.cells = this.el.select('.fc-day',true);
17283         this.textNodes = this.el.query('.fc-day-number');
17284         this.cells.addClassOnOver('fc-state-hover');
17285         
17286         var cells = this.cells.elements;
17287         var textEls = this.textNodes;
17288         
17289         Roo.each(cells, function(cell){
17290             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17291         });
17292         
17293         days += startingPos;
17294
17295         // convert everything to numbers so it's fast
17296         var day = 86400000;
17297         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17298         //Roo.log(d);
17299         //Roo.log(pm);
17300         //Roo.log(prevStart);
17301         
17302         var today = new Date().clearTime().getTime();
17303         var sel = date.clearTime().getTime();
17304         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17305         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17306         var ddMatch = this.disabledDatesRE;
17307         var ddText = this.disabledDatesText;
17308         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17309         var ddaysText = this.disabledDaysText;
17310         var format = this.format;
17311         
17312         var setCellClass = function(cal, cell){
17313             cell.row = 0;
17314             cell.events = [];
17315             cell.more = [];
17316             //Roo.log('set Cell Class');
17317             cell.title = "";
17318             var t = d.getTime();
17319             
17320             //Roo.log(d);
17321             
17322             cell.dateValue = t;
17323             if(t == today){
17324                 cell.className += " fc-today";
17325                 cell.className += " fc-state-highlight";
17326                 cell.title = cal.todayText;
17327             }
17328             if(t == sel){
17329                 // disable highlight in other month..
17330                 //cell.className += " fc-state-highlight";
17331                 
17332             }
17333             // disabling
17334             if(t < min) {
17335                 cell.className = " fc-state-disabled";
17336                 cell.title = cal.minText;
17337                 return;
17338             }
17339             if(t > max) {
17340                 cell.className = " fc-state-disabled";
17341                 cell.title = cal.maxText;
17342                 return;
17343             }
17344             if(ddays){
17345                 if(ddays.indexOf(d.getDay()) != -1){
17346                     cell.title = ddaysText;
17347                     cell.className = " fc-state-disabled";
17348                 }
17349             }
17350             if(ddMatch && format){
17351                 var fvalue = d.dateFormat(format);
17352                 if(ddMatch.test(fvalue)){
17353                     cell.title = ddText.replace("%0", fvalue);
17354                     cell.className = " fc-state-disabled";
17355                 }
17356             }
17357             
17358             if (!cell.initialClassName) {
17359                 cell.initialClassName = cell.dom.className;
17360             }
17361             
17362             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17363         };
17364
17365         var i = 0;
17366         
17367         for(; i < startingPos; i++) {
17368             textEls[i].innerHTML = (++prevStart);
17369             d.setDate(d.getDate()+1);
17370             
17371             cells[i].className = "fc-past fc-other-month";
17372             setCellClass(this, cells[i]);
17373         }
17374         
17375         var intDay = 0;
17376         
17377         for(; i < days; i++){
17378             intDay = i - startingPos + 1;
17379             textEls[i].innerHTML = (intDay);
17380             d.setDate(d.getDate()+1);
17381             
17382             cells[i].className = ''; // "x-date-active";
17383             setCellClass(this, cells[i]);
17384         }
17385         var extraDays = 0;
17386         
17387         for(; i < 42; i++) {
17388             textEls[i].innerHTML = (++extraDays);
17389             d.setDate(d.getDate()+1);
17390             
17391             cells[i].className = "fc-future fc-other-month";
17392             setCellClass(this, cells[i]);
17393         }
17394         
17395         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17396         
17397         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17398         
17399         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17400         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17401         
17402         if(totalRows != 6){
17403             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17404             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17405         }
17406         
17407         this.fireEvent('monthchange', this, date);
17408         
17409         
17410         /*
17411         if(!this.internalRender){
17412             var main = this.el.dom.firstChild;
17413             var w = main.offsetWidth;
17414             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17415             Roo.fly(main).setWidth(w);
17416             this.internalRender = true;
17417             // opera does not respect the auto grow header center column
17418             // then, after it gets a width opera refuses to recalculate
17419             // without a second pass
17420             if(Roo.isOpera && !this.secondPass){
17421                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17422                 this.secondPass = true;
17423                 this.update.defer(10, this, [date]);
17424             }
17425         }
17426         */
17427         
17428     },
17429     
17430     findCell : function(dt) {
17431         dt = dt.clearTime().getTime();
17432         var ret = false;
17433         this.cells.each(function(c){
17434             //Roo.log("check " +c.dateValue + '?=' + dt);
17435             if(c.dateValue == dt){
17436                 ret = c;
17437                 return false;
17438             }
17439             return true;
17440         });
17441         
17442         return ret;
17443     },
17444     
17445     findCells : function(ev) {
17446         var s = ev.start.clone().clearTime().getTime();
17447        // Roo.log(s);
17448         var e= ev.end.clone().clearTime().getTime();
17449        // Roo.log(e);
17450         var ret = [];
17451         this.cells.each(function(c){
17452              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17453             
17454             if(c.dateValue > e){
17455                 return ;
17456             }
17457             if(c.dateValue < s){
17458                 return ;
17459             }
17460             ret.push(c);
17461         });
17462         
17463         return ret;    
17464     },
17465     
17466 //    findBestRow: function(cells)
17467 //    {
17468 //        var ret = 0;
17469 //        
17470 //        for (var i =0 ; i < cells.length;i++) {
17471 //            ret  = Math.max(cells[i].rows || 0,ret);
17472 //        }
17473 //        return ret;
17474 //        
17475 //    },
17476     
17477     
17478     addItem : function(ev)
17479     {
17480         // look for vertical location slot in
17481         var cells = this.findCells(ev);
17482         
17483 //        ev.row = this.findBestRow(cells);
17484         
17485         // work out the location.
17486         
17487         var crow = false;
17488         var rows = [];
17489         for(var i =0; i < cells.length; i++) {
17490             
17491             cells[i].row = cells[0].row;
17492             
17493             if(i == 0){
17494                 cells[i].row = cells[i].row + 1;
17495             }
17496             
17497             if (!crow) {
17498                 crow = {
17499                     start : cells[i],
17500                     end :  cells[i]
17501                 };
17502                 continue;
17503             }
17504             if (crow.start.getY() == cells[i].getY()) {
17505                 // on same row.
17506                 crow.end = cells[i];
17507                 continue;
17508             }
17509             // different row.
17510             rows.push(crow);
17511             crow = {
17512                 start: cells[i],
17513                 end : cells[i]
17514             };
17515             
17516         }
17517         
17518         rows.push(crow);
17519         ev.els = [];
17520         ev.rows = rows;
17521         ev.cells = cells;
17522         
17523         cells[0].events.push(ev);
17524         
17525         this.calevents.push(ev);
17526     },
17527     
17528     clearEvents: function() {
17529         
17530         if(!this.calevents){
17531             return;
17532         }
17533         
17534         Roo.each(this.cells.elements, function(c){
17535             c.row = 0;
17536             c.events = [];
17537             c.more = [];
17538         });
17539         
17540         Roo.each(this.calevents, function(e) {
17541             Roo.each(e.els, function(el) {
17542                 el.un('mouseenter' ,this.onEventEnter, this);
17543                 el.un('mouseleave' ,this.onEventLeave, this);
17544                 el.remove();
17545             },this);
17546         },this);
17547         
17548         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17549             e.remove();
17550         });
17551         
17552     },
17553     
17554     renderEvents: function()
17555     {   
17556         var _this = this;
17557         
17558         this.cells.each(function(c) {
17559             
17560             if(c.row < 5){
17561                 return;
17562             }
17563             
17564             var ev = c.events;
17565             
17566             var r = 4;
17567             if(c.row != c.events.length){
17568                 r = 4 - (4 - (c.row - c.events.length));
17569             }
17570             
17571             c.events = ev.slice(0, r);
17572             c.more = ev.slice(r);
17573             
17574             if(c.more.length && c.more.length == 1){
17575                 c.events.push(c.more.pop());
17576             }
17577             
17578             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17579             
17580         });
17581             
17582         this.cells.each(function(c) {
17583             
17584             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17585             
17586             
17587             for (var e = 0; e < c.events.length; e++){
17588                 var ev = c.events[e];
17589                 var rows = ev.rows;
17590                 
17591                 for(var i = 0; i < rows.length; i++) {
17592                 
17593                     // how many rows should it span..
17594
17595                     var  cfg = {
17596                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17597                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17598
17599                         unselectable : "on",
17600                         cn : [
17601                             {
17602                                 cls: 'fc-event-inner',
17603                                 cn : [
17604     //                                {
17605     //                                  tag:'span',
17606     //                                  cls: 'fc-event-time',
17607     //                                  html : cells.length > 1 ? '' : ev.time
17608     //                                },
17609                                     {
17610                                       tag:'span',
17611                                       cls: 'fc-event-title',
17612                                       html : String.format('{0}', ev.title)
17613                                     }
17614
17615
17616                                 ]
17617                             },
17618                             {
17619                                 cls: 'ui-resizable-handle ui-resizable-e',
17620                                 html : '&nbsp;&nbsp;&nbsp'
17621                             }
17622
17623                         ]
17624                     };
17625
17626                     if (i == 0) {
17627                         cfg.cls += ' fc-event-start';
17628                     }
17629                     if ((i+1) == rows.length) {
17630                         cfg.cls += ' fc-event-end';
17631                     }
17632
17633                     var ctr = _this.el.select('.fc-event-container',true).first();
17634                     var cg = ctr.createChild(cfg);
17635
17636                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17637                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17638
17639                     var r = (c.more.length) ? 1 : 0;
17640                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17641                     cg.setWidth(ebox.right - sbox.x -2);
17642
17643                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17644                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17645                     cg.on('click', _this.onEventClick, _this, ev);
17646
17647                     ev.els.push(cg);
17648                     
17649                 }
17650                 
17651             }
17652             
17653             
17654             if(c.more.length){
17655                 var  cfg = {
17656                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17657                     style : 'position: absolute',
17658                     unselectable : "on",
17659                     cn : [
17660                         {
17661                             cls: 'fc-event-inner',
17662                             cn : [
17663                                 {
17664                                   tag:'span',
17665                                   cls: 'fc-event-title',
17666                                   html : 'More'
17667                                 }
17668
17669
17670                             ]
17671                         },
17672                         {
17673                             cls: 'ui-resizable-handle ui-resizable-e',
17674                             html : '&nbsp;&nbsp;&nbsp'
17675                         }
17676
17677                     ]
17678                 };
17679
17680                 var ctr = _this.el.select('.fc-event-container',true).first();
17681                 var cg = ctr.createChild(cfg);
17682
17683                 var sbox = c.select('.fc-day-content',true).first().getBox();
17684                 var ebox = c.select('.fc-day-content',true).first().getBox();
17685                 //Roo.log(cg);
17686                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17687                 cg.setWidth(ebox.right - sbox.x -2);
17688
17689                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17690                 
17691             }
17692             
17693         });
17694         
17695         
17696         
17697     },
17698     
17699     onEventEnter: function (e, el,event,d) {
17700         this.fireEvent('evententer', this, el, event);
17701     },
17702     
17703     onEventLeave: function (e, el,event,d) {
17704         this.fireEvent('eventleave', this, el, event);
17705     },
17706     
17707     onEventClick: function (e, el,event,d) {
17708         this.fireEvent('eventclick', this, el, event);
17709     },
17710     
17711     onMonthChange: function () {
17712         this.store.load();
17713     },
17714     
17715     onMoreEventClick: function(e, el, more)
17716     {
17717         var _this = this;
17718         
17719         this.calpopover.placement = 'right';
17720         this.calpopover.setTitle('More');
17721         
17722         this.calpopover.setContent('');
17723         
17724         var ctr = this.calpopover.el.select('.popover-content', true).first();
17725         
17726         Roo.each(more, function(m){
17727             var cfg = {
17728                 cls : 'fc-event-hori fc-event-draggable',
17729                 html : m.title
17730             };
17731             var cg = ctr.createChild(cfg);
17732             
17733             cg.on('click', _this.onEventClick, _this, m);
17734         });
17735         
17736         this.calpopover.show(el);
17737         
17738         
17739     },
17740     
17741     onLoad: function () 
17742     {   
17743         this.calevents = [];
17744         var cal = this;
17745         
17746         if(this.store.getCount() > 0){
17747             this.store.data.each(function(d){
17748                cal.addItem({
17749                     id : d.data.id,
17750                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17751                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17752                     time : d.data.start_time,
17753                     title : d.data.title,
17754                     description : d.data.description,
17755                     venue : d.data.venue
17756                 });
17757             });
17758         }
17759         
17760         this.renderEvents();
17761         
17762         if(this.calevents.length && this.loadMask){
17763             this.maskEl.hide();
17764         }
17765     },
17766     
17767     onBeforeLoad: function()
17768     {
17769         this.clearEvents();
17770         if(this.loadMask){
17771             this.maskEl.show();
17772         }
17773     }
17774 });
17775
17776  
17777  /*
17778  * - LGPL
17779  *
17780  * element
17781  * 
17782  */
17783
17784 /**
17785  * @class Roo.bootstrap.Popover
17786  * @extends Roo.bootstrap.Component
17787  * Bootstrap Popover class
17788  * @cfg {String} html contents of the popover   (or false to use children..)
17789  * @cfg {String} title of popover (or false to hide)
17790  * @cfg {String} placement how it is placed
17791  * @cfg {String} trigger click || hover (or false to trigger manually)
17792  * @cfg {String} over what (parent or false to trigger manually.)
17793  * @cfg {Number} delay - delay before showing
17794  
17795  * @constructor
17796  * Create a new Popover
17797  * @param {Object} config The config object
17798  */
17799
17800 Roo.bootstrap.Popover = function(config){
17801     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17802     
17803     this.addEvents({
17804         // raw events
17805          /**
17806          * @event show
17807          * After the popover show
17808          * 
17809          * @param {Roo.bootstrap.Popover} this
17810          */
17811         "show" : true,
17812         /**
17813          * @event hide
17814          * After the popover hide
17815          * 
17816          * @param {Roo.bootstrap.Popover} this
17817          */
17818         "hide" : true
17819     });
17820 };
17821
17822 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17823     
17824     title: 'Fill in a title',
17825     html: false,
17826     
17827     placement : 'right',
17828     trigger : 'hover', // hover
17829     
17830     delay : 0,
17831     
17832     over: 'parent',
17833     
17834     can_build_overlaid : false,
17835     
17836     getChildContainer : function()
17837     {
17838         return this.el.select('.popover-content',true).first();
17839     },
17840     
17841     getAutoCreate : function(){
17842          
17843         var cfg = {
17844            cls : 'popover roo-dynamic',
17845            style: 'display:block',
17846            cn : [
17847                 {
17848                     cls : 'arrow'
17849                 },
17850                 {
17851                     cls : 'popover-inner',
17852                     cn : [
17853                         {
17854                             tag: 'h3',
17855                             cls: 'popover-title popover-header',
17856                             html : this.title
17857                         },
17858                         {
17859                             cls : 'popover-content popover-body',
17860                             html : this.html
17861                         }
17862                     ]
17863                     
17864                 }
17865            ]
17866         };
17867         
17868         return cfg;
17869     },
17870     setTitle: function(str)
17871     {
17872         this.title = str;
17873         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17874     },
17875     setContent: function(str)
17876     {
17877         this.html = str;
17878         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17879     },
17880     // as it get's added to the bottom of the page.
17881     onRender : function(ct, position)
17882     {
17883         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17884         if(!this.el){
17885             var cfg = Roo.apply({},  this.getAutoCreate());
17886             cfg.id = Roo.id();
17887             
17888             if (this.cls) {
17889                 cfg.cls += ' ' + this.cls;
17890             }
17891             if (this.style) {
17892                 cfg.style = this.style;
17893             }
17894             //Roo.log("adding to ");
17895             this.el = Roo.get(document.body).createChild(cfg, position);
17896 //            Roo.log(this.el);
17897         }
17898         this.initEvents();
17899     },
17900     
17901     initEvents : function()
17902     {
17903         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17904         this.el.enableDisplayMode('block');
17905         this.el.hide();
17906         if (this.over === false) {
17907             return; 
17908         }
17909         if (this.triggers === false) {
17910             return;
17911         }
17912         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17913         var triggers = this.trigger ? this.trigger.split(' ') : [];
17914         Roo.each(triggers, function(trigger) {
17915         
17916             if (trigger == 'click') {
17917                 on_el.on('click', this.toggle, this);
17918             } else if (trigger != 'manual') {
17919                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17920                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17921       
17922                 on_el.on(eventIn  ,this.enter, this);
17923                 on_el.on(eventOut, this.leave, this);
17924             }
17925         }, this);
17926         
17927     },
17928     
17929     
17930     // private
17931     timeout : null,
17932     hoverState : null,
17933     
17934     toggle : function () {
17935         this.hoverState == 'in' ? this.leave() : this.enter();
17936     },
17937     
17938     enter : function () {
17939         
17940         clearTimeout(this.timeout);
17941     
17942         this.hoverState = 'in';
17943     
17944         if (!this.delay || !this.delay.show) {
17945             this.show();
17946             return;
17947         }
17948         var _t = this;
17949         this.timeout = setTimeout(function () {
17950             if (_t.hoverState == 'in') {
17951                 _t.show();
17952             }
17953         }, this.delay.show)
17954     },
17955     
17956     leave : function() {
17957         clearTimeout(this.timeout);
17958     
17959         this.hoverState = 'out';
17960     
17961         if (!this.delay || !this.delay.hide) {
17962             this.hide();
17963             return;
17964         }
17965         var _t = this;
17966         this.timeout = setTimeout(function () {
17967             if (_t.hoverState == 'out') {
17968                 _t.hide();
17969             }
17970         }, this.delay.hide)
17971     },
17972     
17973     show : function (on_el)
17974     {
17975         if (!on_el) {
17976             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17977         }
17978         
17979         // set content.
17980         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17981         if (this.html !== false) {
17982             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17983         }
17984         this.el.removeClass([
17985             'fade','top','bottom', 'left', 'right','in',
17986             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17987         ]);
17988         if (!this.title.length) {
17989             this.el.select('.popover-title',true).hide();
17990         }
17991         
17992         var placement = typeof this.placement == 'function' ?
17993             this.placement.call(this, this.el, on_el) :
17994             this.placement;
17995             
17996         var autoToken = /\s?auto?\s?/i;
17997         var autoPlace = autoToken.test(placement);
17998         if (autoPlace) {
17999             placement = placement.replace(autoToken, '') || 'top';
18000         }
18001         
18002         //this.el.detach()
18003         //this.el.setXY([0,0]);
18004         this.el.show();
18005         this.el.dom.style.display='block';
18006         this.el.addClass(placement);
18007         
18008         //this.el.appendTo(on_el);
18009         
18010         var p = this.getPosition();
18011         var box = this.el.getBox();
18012         
18013         if (autoPlace) {
18014             // fixme..
18015         }
18016         var align = Roo.bootstrap.Popover.alignment[placement];
18017         
18018 //        Roo.log(align);
18019         this.el.alignTo(on_el, align[0],align[1]);
18020         //var arrow = this.el.select('.arrow',true).first();
18021         //arrow.set(align[2], 
18022         
18023         this.el.addClass('in');
18024         
18025         
18026         if (this.el.hasClass('fade')) {
18027             // fade it?
18028         }
18029         
18030         this.hoverState = 'in';
18031         
18032         this.fireEvent('show', this);
18033         
18034     },
18035     hide : function()
18036     {
18037         this.el.setXY([0,0]);
18038         this.el.removeClass('in');
18039         this.el.hide();
18040         this.hoverState = null;
18041         
18042         this.fireEvent('hide', this);
18043     }
18044     
18045 });
18046
18047 Roo.bootstrap.Popover.alignment = {
18048     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18049     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18050     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18051     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18052 };
18053
18054  /*
18055  * - LGPL
18056  *
18057  * Progress
18058  * 
18059  */
18060
18061 /**
18062  * @class Roo.bootstrap.Progress
18063  * @extends Roo.bootstrap.Component
18064  * Bootstrap Progress class
18065  * @cfg {Boolean} striped striped of the progress bar
18066  * @cfg {Boolean} active animated of the progress bar
18067  * 
18068  * 
18069  * @constructor
18070  * Create a new Progress
18071  * @param {Object} config The config object
18072  */
18073
18074 Roo.bootstrap.Progress = function(config){
18075     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18076 };
18077
18078 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18079     
18080     striped : false,
18081     active: false,
18082     
18083     getAutoCreate : function(){
18084         var cfg = {
18085             tag: 'div',
18086             cls: 'progress'
18087         };
18088         
18089         
18090         if(this.striped){
18091             cfg.cls += ' progress-striped';
18092         }
18093       
18094         if(this.active){
18095             cfg.cls += ' active';
18096         }
18097         
18098         
18099         return cfg;
18100     }
18101    
18102 });
18103
18104  
18105
18106  /*
18107  * - LGPL
18108  *
18109  * ProgressBar
18110  * 
18111  */
18112
18113 /**
18114  * @class Roo.bootstrap.ProgressBar
18115  * @extends Roo.bootstrap.Component
18116  * Bootstrap ProgressBar class
18117  * @cfg {Number} aria_valuenow aria-value now
18118  * @cfg {Number} aria_valuemin aria-value min
18119  * @cfg {Number} aria_valuemax aria-value max
18120  * @cfg {String} label label for the progress bar
18121  * @cfg {String} panel (success | info | warning | danger )
18122  * @cfg {String} role role of the progress bar
18123  * @cfg {String} sr_only text
18124  * 
18125  * 
18126  * @constructor
18127  * Create a new ProgressBar
18128  * @param {Object} config The config object
18129  */
18130
18131 Roo.bootstrap.ProgressBar = function(config){
18132     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18133 };
18134
18135 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18136     
18137     aria_valuenow : 0,
18138     aria_valuemin : 0,
18139     aria_valuemax : 100,
18140     label : false,
18141     panel : false,
18142     role : false,
18143     sr_only: false,
18144     
18145     getAutoCreate : function()
18146     {
18147         
18148         var cfg = {
18149             tag: 'div',
18150             cls: 'progress-bar',
18151             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18152         };
18153         
18154         if(this.sr_only){
18155             cfg.cn = {
18156                 tag: 'span',
18157                 cls: 'sr-only',
18158                 html: this.sr_only
18159             }
18160         }
18161         
18162         if(this.role){
18163             cfg.role = this.role;
18164         }
18165         
18166         if(this.aria_valuenow){
18167             cfg['aria-valuenow'] = this.aria_valuenow;
18168         }
18169         
18170         if(this.aria_valuemin){
18171             cfg['aria-valuemin'] = this.aria_valuemin;
18172         }
18173         
18174         if(this.aria_valuemax){
18175             cfg['aria-valuemax'] = this.aria_valuemax;
18176         }
18177         
18178         if(this.label && !this.sr_only){
18179             cfg.html = this.label;
18180         }
18181         
18182         if(this.panel){
18183             cfg.cls += ' progress-bar-' + this.panel;
18184         }
18185         
18186         return cfg;
18187     },
18188     
18189     update : function(aria_valuenow)
18190     {
18191         this.aria_valuenow = aria_valuenow;
18192         
18193         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18194     }
18195    
18196 });
18197
18198  
18199
18200  /*
18201  * - LGPL
18202  *
18203  * column
18204  * 
18205  */
18206
18207 /**
18208  * @class Roo.bootstrap.TabGroup
18209  * @extends Roo.bootstrap.Column
18210  * Bootstrap Column class
18211  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18212  * @cfg {Boolean} carousel true to make the group behave like a carousel
18213  * @cfg {Boolean} bullets show bullets for the panels
18214  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18215  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18216  * @cfg {Boolean} showarrow (true|false) show arrow default true
18217  * 
18218  * @constructor
18219  * Create a new TabGroup
18220  * @param {Object} config The config object
18221  */
18222
18223 Roo.bootstrap.TabGroup = function(config){
18224     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18225     if (!this.navId) {
18226         this.navId = Roo.id();
18227     }
18228     this.tabs = [];
18229     Roo.bootstrap.TabGroup.register(this);
18230     
18231 };
18232
18233 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18234     
18235     carousel : false,
18236     transition : false,
18237     bullets : 0,
18238     timer : 0,
18239     autoslide : false,
18240     slideFn : false,
18241     slideOnTouch : false,
18242     showarrow : true,
18243     
18244     getAutoCreate : function()
18245     {
18246         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18247         
18248         cfg.cls += ' tab-content';
18249         
18250         if (this.carousel) {
18251             cfg.cls += ' carousel slide';
18252             
18253             cfg.cn = [{
18254                cls : 'carousel-inner',
18255                cn : []
18256             }];
18257         
18258             if(this.bullets  && !Roo.isTouch){
18259                 
18260                 var bullets = {
18261                     cls : 'carousel-bullets',
18262                     cn : []
18263                 };
18264                
18265                 if(this.bullets_cls){
18266                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18267                 }
18268                 
18269                 bullets.cn.push({
18270                     cls : 'clear'
18271                 });
18272                 
18273                 cfg.cn[0].cn.push(bullets);
18274             }
18275             
18276             if(this.showarrow){
18277                 cfg.cn[0].cn.push({
18278                     tag : 'div',
18279                     class : 'carousel-arrow',
18280                     cn : [
18281                         {
18282                             tag : 'div',
18283                             class : 'carousel-prev',
18284                             cn : [
18285                                 {
18286                                     tag : 'i',
18287                                     class : 'fa fa-chevron-left'
18288                                 }
18289                             ]
18290                         },
18291                         {
18292                             tag : 'div',
18293                             class : 'carousel-next',
18294                             cn : [
18295                                 {
18296                                     tag : 'i',
18297                                     class : 'fa fa-chevron-right'
18298                                 }
18299                             ]
18300                         }
18301                     ]
18302                 });
18303             }
18304             
18305         }
18306         
18307         return cfg;
18308     },
18309     
18310     initEvents:  function()
18311     {
18312 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18313 //            this.el.on("touchstart", this.onTouchStart, this);
18314 //        }
18315         
18316         if(this.autoslide){
18317             var _this = this;
18318             
18319             this.slideFn = window.setInterval(function() {
18320                 _this.showPanelNext();
18321             }, this.timer);
18322         }
18323         
18324         if(this.showarrow){
18325             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18326             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18327         }
18328         
18329         
18330     },
18331     
18332 //    onTouchStart : function(e, el, o)
18333 //    {
18334 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18335 //            return;
18336 //        }
18337 //        
18338 //        this.showPanelNext();
18339 //    },
18340     
18341     
18342     getChildContainer : function()
18343     {
18344         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18345     },
18346     
18347     /**
18348     * register a Navigation item
18349     * @param {Roo.bootstrap.NavItem} the navitem to add
18350     */
18351     register : function(item)
18352     {
18353         this.tabs.push( item);
18354         item.navId = this.navId; // not really needed..
18355         this.addBullet();
18356     
18357     },
18358     
18359     getActivePanel : function()
18360     {
18361         var r = false;
18362         Roo.each(this.tabs, function(t) {
18363             if (t.active) {
18364                 r = t;
18365                 return false;
18366             }
18367             return null;
18368         });
18369         return r;
18370         
18371     },
18372     getPanelByName : function(n)
18373     {
18374         var r = false;
18375         Roo.each(this.tabs, function(t) {
18376             if (t.tabId == n) {
18377                 r = t;
18378                 return false;
18379             }
18380             return null;
18381         });
18382         return r;
18383     },
18384     indexOfPanel : function(p)
18385     {
18386         var r = false;
18387         Roo.each(this.tabs, function(t,i) {
18388             if (t.tabId == p.tabId) {
18389                 r = i;
18390                 return false;
18391             }
18392             return null;
18393         });
18394         return r;
18395     },
18396     /**
18397      * show a specific panel
18398      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18399      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18400      */
18401     showPanel : function (pan)
18402     {
18403         if(this.transition || typeof(pan) == 'undefined'){
18404             Roo.log("waiting for the transitionend");
18405             return false;
18406         }
18407         
18408         if (typeof(pan) == 'number') {
18409             pan = this.tabs[pan];
18410         }
18411         
18412         if (typeof(pan) == 'string') {
18413             pan = this.getPanelByName(pan);
18414         }
18415         
18416         var cur = this.getActivePanel();
18417         
18418         if(!pan || !cur){
18419             Roo.log('pan or acitve pan is undefined');
18420             return false;
18421         }
18422         
18423         if (pan.tabId == this.getActivePanel().tabId) {
18424             return true;
18425         }
18426         
18427         if (false === cur.fireEvent('beforedeactivate')) {
18428             return false;
18429         }
18430         
18431         if(this.bullets > 0 && !Roo.isTouch){
18432             this.setActiveBullet(this.indexOfPanel(pan));
18433         }
18434         
18435         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18436             
18437             //class="carousel-item carousel-item-next carousel-item-left"
18438             
18439             this.transition = true;
18440             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18441             var lr = dir == 'next' ? 'left' : 'right';
18442             pan.el.addClass(dir); // or prev
18443             pan.el.addClass('carousel-item-' + dir); // or prev
18444             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18445             cur.el.addClass(lr); // or right
18446             pan.el.addClass(lr);
18447             cur.el.addClass('carousel-item-' +lr); // or right
18448             pan.el.addClass('carousel-item-' +lr);
18449             
18450             
18451             var _this = this;
18452             cur.el.on('transitionend', function() {
18453                 Roo.log("trans end?");
18454                 
18455                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18456                 pan.setActive(true);
18457                 
18458                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18459                 cur.setActive(false);
18460                 
18461                 _this.transition = false;
18462                 
18463             }, this, { single:  true } );
18464             
18465             return true;
18466         }
18467         
18468         cur.setActive(false);
18469         pan.setActive(true);
18470         
18471         return true;
18472         
18473     },
18474     showPanelNext : function()
18475     {
18476         var i = this.indexOfPanel(this.getActivePanel());
18477         
18478         if (i >= this.tabs.length - 1 && !this.autoslide) {
18479             return;
18480         }
18481         
18482         if (i >= this.tabs.length - 1 && this.autoslide) {
18483             i = -1;
18484         }
18485         
18486         this.showPanel(this.tabs[i+1]);
18487     },
18488     
18489     showPanelPrev : function()
18490     {
18491         var i = this.indexOfPanel(this.getActivePanel());
18492         
18493         if (i  < 1 && !this.autoslide) {
18494             return;
18495         }
18496         
18497         if (i < 1 && this.autoslide) {
18498             i = this.tabs.length;
18499         }
18500         
18501         this.showPanel(this.tabs[i-1]);
18502     },
18503     
18504     
18505     addBullet: function()
18506     {
18507         if(!this.bullets || Roo.isTouch){
18508             return;
18509         }
18510         var ctr = this.el.select('.carousel-bullets',true).first();
18511         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18512         var bullet = ctr.createChild({
18513             cls : 'bullet bullet-' + i
18514         },ctr.dom.lastChild);
18515         
18516         
18517         var _this = this;
18518         
18519         bullet.on('click', (function(e, el, o, ii, t){
18520
18521             e.preventDefault();
18522
18523             this.showPanel(ii);
18524
18525             if(this.autoslide && this.slideFn){
18526                 clearInterval(this.slideFn);
18527                 this.slideFn = window.setInterval(function() {
18528                     _this.showPanelNext();
18529                 }, this.timer);
18530             }
18531
18532         }).createDelegate(this, [i, bullet], true));
18533                 
18534         
18535     },
18536      
18537     setActiveBullet : function(i)
18538     {
18539         if(Roo.isTouch){
18540             return;
18541         }
18542         
18543         Roo.each(this.el.select('.bullet', true).elements, function(el){
18544             el.removeClass('selected');
18545         });
18546
18547         var bullet = this.el.select('.bullet-' + i, true).first();
18548         
18549         if(!bullet){
18550             return;
18551         }
18552         
18553         bullet.addClass('selected');
18554     }
18555     
18556     
18557   
18558 });
18559
18560  
18561
18562  
18563  
18564 Roo.apply(Roo.bootstrap.TabGroup, {
18565     
18566     groups: {},
18567      /**
18568     * register a Navigation Group
18569     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18570     */
18571     register : function(navgrp)
18572     {
18573         this.groups[navgrp.navId] = navgrp;
18574         
18575     },
18576     /**
18577     * fetch a Navigation Group based on the navigation ID
18578     * if one does not exist , it will get created.
18579     * @param {string} the navgroup to add
18580     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18581     */
18582     get: function(navId) {
18583         if (typeof(this.groups[navId]) == 'undefined') {
18584             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18585         }
18586         return this.groups[navId] ;
18587     }
18588     
18589     
18590     
18591 });
18592
18593  /*
18594  * - LGPL
18595  *
18596  * TabPanel
18597  * 
18598  */
18599
18600 /**
18601  * @class Roo.bootstrap.TabPanel
18602  * @extends Roo.bootstrap.Component
18603  * Bootstrap TabPanel class
18604  * @cfg {Boolean} active panel active
18605  * @cfg {String} html panel content
18606  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18607  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18608  * @cfg {String} href click to link..
18609  * 
18610  * 
18611  * @constructor
18612  * Create a new TabPanel
18613  * @param {Object} config The config object
18614  */
18615
18616 Roo.bootstrap.TabPanel = function(config){
18617     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18618     this.addEvents({
18619         /**
18620              * @event changed
18621              * Fires when the active status changes
18622              * @param {Roo.bootstrap.TabPanel} this
18623              * @param {Boolean} state the new state
18624             
18625          */
18626         'changed': true,
18627         /**
18628              * @event beforedeactivate
18629              * Fires before a tab is de-activated - can be used to do validation on a form.
18630              * @param {Roo.bootstrap.TabPanel} this
18631              * @return {Boolean} false if there is an error
18632             
18633          */
18634         'beforedeactivate': true
18635      });
18636     
18637     this.tabId = this.tabId || Roo.id();
18638   
18639 };
18640
18641 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18642     
18643     active: false,
18644     html: false,
18645     tabId: false,
18646     navId : false,
18647     href : '',
18648     
18649     getAutoCreate : function(){
18650         
18651         
18652         var cfg = {
18653             tag: 'div',
18654             // item is needed for carousel - not sure if it has any effect otherwise
18655             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18656             html: this.html || ''
18657         };
18658         
18659         if(this.active){
18660             cfg.cls += ' active';
18661         }
18662         
18663         if(this.tabId){
18664             cfg.tabId = this.tabId;
18665         }
18666         
18667         
18668         
18669         return cfg;
18670     },
18671     
18672     initEvents:  function()
18673     {
18674         var p = this.parent();
18675         
18676         this.navId = this.navId || p.navId;
18677         
18678         if (typeof(this.navId) != 'undefined') {
18679             // not really needed.. but just in case.. parent should be a NavGroup.
18680             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18681             
18682             tg.register(this);
18683             
18684             var i = tg.tabs.length - 1;
18685             
18686             if(this.active && tg.bullets > 0 && i < tg.bullets){
18687                 tg.setActiveBullet(i);
18688             }
18689         }
18690         
18691         this.el.on('click', this.onClick, this);
18692         
18693         if(Roo.isTouch){
18694             this.el.on("touchstart", this.onTouchStart, this);
18695             this.el.on("touchmove", this.onTouchMove, this);
18696             this.el.on("touchend", this.onTouchEnd, this);
18697         }
18698         
18699     },
18700     
18701     onRender : function(ct, position)
18702     {
18703         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18704     },
18705     
18706     setActive : function(state)
18707     {
18708         Roo.log("panel - set active " + this.tabId + "=" + state);
18709         
18710         this.active = state;
18711         if (!state) {
18712             this.el.removeClass('active');
18713             
18714         } else  if (!this.el.hasClass('active')) {
18715             this.el.addClass('active');
18716         }
18717         
18718         this.fireEvent('changed', this, state);
18719     },
18720     
18721     onClick : function(e)
18722     {
18723         e.preventDefault();
18724         
18725         if(!this.href.length){
18726             return;
18727         }
18728         
18729         window.location.href = this.href;
18730     },
18731     
18732     startX : 0,
18733     startY : 0,
18734     endX : 0,
18735     endY : 0,
18736     swiping : false,
18737     
18738     onTouchStart : function(e)
18739     {
18740         this.swiping = false;
18741         
18742         this.startX = e.browserEvent.touches[0].clientX;
18743         this.startY = e.browserEvent.touches[0].clientY;
18744     },
18745     
18746     onTouchMove : function(e)
18747     {
18748         this.swiping = true;
18749         
18750         this.endX = e.browserEvent.touches[0].clientX;
18751         this.endY = e.browserEvent.touches[0].clientY;
18752     },
18753     
18754     onTouchEnd : function(e)
18755     {
18756         if(!this.swiping){
18757             this.onClick(e);
18758             return;
18759         }
18760         
18761         var tabGroup = this.parent();
18762         
18763         if(this.endX > this.startX){ // swiping right
18764             tabGroup.showPanelPrev();
18765             return;
18766         }
18767         
18768         if(this.startX > this.endX){ // swiping left
18769             tabGroup.showPanelNext();
18770             return;
18771         }
18772     }
18773     
18774     
18775 });
18776  
18777
18778  
18779
18780  /*
18781  * - LGPL
18782  *
18783  * DateField
18784  * 
18785  */
18786
18787 /**
18788  * @class Roo.bootstrap.DateField
18789  * @extends Roo.bootstrap.Input
18790  * Bootstrap DateField class
18791  * @cfg {Number} weekStart default 0
18792  * @cfg {String} viewMode default empty, (months|years)
18793  * @cfg {String} minViewMode default empty, (months|years)
18794  * @cfg {Number} startDate default -Infinity
18795  * @cfg {Number} endDate default Infinity
18796  * @cfg {Boolean} todayHighlight default false
18797  * @cfg {Boolean} todayBtn default false
18798  * @cfg {Boolean} calendarWeeks default false
18799  * @cfg {Object} daysOfWeekDisabled default empty
18800  * @cfg {Boolean} singleMode default false (true | false)
18801  * 
18802  * @cfg {Boolean} keyboardNavigation default true
18803  * @cfg {String} language default en
18804  * 
18805  * @constructor
18806  * Create a new DateField
18807  * @param {Object} config The config object
18808  */
18809
18810 Roo.bootstrap.DateField = function(config){
18811     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18812      this.addEvents({
18813             /**
18814              * @event show
18815              * Fires when this field show.
18816              * @param {Roo.bootstrap.DateField} this
18817              * @param {Mixed} date The date value
18818              */
18819             show : true,
18820             /**
18821              * @event show
18822              * Fires when this field hide.
18823              * @param {Roo.bootstrap.DateField} this
18824              * @param {Mixed} date The date value
18825              */
18826             hide : true,
18827             /**
18828              * @event select
18829              * Fires when select a date.
18830              * @param {Roo.bootstrap.DateField} this
18831              * @param {Mixed} date The date value
18832              */
18833             select : true,
18834             /**
18835              * @event beforeselect
18836              * Fires when before select a date.
18837              * @param {Roo.bootstrap.DateField} this
18838              * @param {Mixed} date The date value
18839              */
18840             beforeselect : true
18841         });
18842 };
18843
18844 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18845     
18846     /**
18847      * @cfg {String} format
18848      * The default date format string which can be overriden for localization support.  The format must be
18849      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18850      */
18851     format : "m/d/y",
18852     /**
18853      * @cfg {String} altFormats
18854      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18855      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18856      */
18857     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18858     
18859     weekStart : 0,
18860     
18861     viewMode : '',
18862     
18863     minViewMode : '',
18864     
18865     todayHighlight : false,
18866     
18867     todayBtn: false,
18868     
18869     language: 'en',
18870     
18871     keyboardNavigation: true,
18872     
18873     calendarWeeks: false,
18874     
18875     startDate: -Infinity,
18876     
18877     endDate: Infinity,
18878     
18879     daysOfWeekDisabled: [],
18880     
18881     _events: [],
18882     
18883     singleMode : false,
18884     
18885     UTCDate: function()
18886     {
18887         return new Date(Date.UTC.apply(Date, arguments));
18888     },
18889     
18890     UTCToday: function()
18891     {
18892         var today = new Date();
18893         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18894     },
18895     
18896     getDate: function() {
18897             var d = this.getUTCDate();
18898             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18899     },
18900     
18901     getUTCDate: function() {
18902             return this.date;
18903     },
18904     
18905     setDate: function(d) {
18906             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18907     },
18908     
18909     setUTCDate: function(d) {
18910             this.date = d;
18911             this.setValue(this.formatDate(this.date));
18912     },
18913         
18914     onRender: function(ct, position)
18915     {
18916         
18917         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18918         
18919         this.language = this.language || 'en';
18920         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18921         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18922         
18923         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18924         this.format = this.format || 'm/d/y';
18925         this.isInline = false;
18926         this.isInput = true;
18927         this.component = this.el.select('.add-on', true).first() || false;
18928         this.component = (this.component && this.component.length === 0) ? false : this.component;
18929         this.hasInput = this.component && this.inputEl().length;
18930         
18931         if (typeof(this.minViewMode === 'string')) {
18932             switch (this.minViewMode) {
18933                 case 'months':
18934                     this.minViewMode = 1;
18935                     break;
18936                 case 'years':
18937                     this.minViewMode = 2;
18938                     break;
18939                 default:
18940                     this.minViewMode = 0;
18941                     break;
18942             }
18943         }
18944         
18945         if (typeof(this.viewMode === 'string')) {
18946             switch (this.viewMode) {
18947                 case 'months':
18948                     this.viewMode = 1;
18949                     break;
18950                 case 'years':
18951                     this.viewMode = 2;
18952                     break;
18953                 default:
18954                     this.viewMode = 0;
18955                     break;
18956             }
18957         }
18958                 
18959         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18960         
18961 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18962         
18963         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18964         
18965         this.picker().on('mousedown', this.onMousedown, this);
18966         this.picker().on('click', this.onClick, this);
18967         
18968         this.picker().addClass('datepicker-dropdown');
18969         
18970         this.startViewMode = this.viewMode;
18971         
18972         if(this.singleMode){
18973             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18974                 v.setVisibilityMode(Roo.Element.DISPLAY);
18975                 v.hide();
18976             });
18977             
18978             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18979                 v.setStyle('width', '189px');
18980             });
18981         }
18982         
18983         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18984             if(!this.calendarWeeks){
18985                 v.remove();
18986                 return;
18987             }
18988             
18989             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18990             v.attr('colspan', function(i, val){
18991                 return parseInt(val) + 1;
18992             });
18993         });
18994                         
18995         
18996         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18997         
18998         this.setStartDate(this.startDate);
18999         this.setEndDate(this.endDate);
19000         
19001         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19002         
19003         this.fillDow();
19004         this.fillMonths();
19005         this.update();
19006         this.showMode();
19007         
19008         if(this.isInline) {
19009             this.showPopup();
19010         }
19011     },
19012     
19013     picker : function()
19014     {
19015         return this.pickerEl;
19016 //        return this.el.select('.datepicker', true).first();
19017     },
19018     
19019     fillDow: function()
19020     {
19021         var dowCnt = this.weekStart;
19022         
19023         var dow = {
19024             tag: 'tr',
19025             cn: [
19026                 
19027             ]
19028         };
19029         
19030         if(this.calendarWeeks){
19031             dow.cn.push({
19032                 tag: 'th',
19033                 cls: 'cw',
19034                 html: '&nbsp;'
19035             })
19036         }
19037         
19038         while (dowCnt < this.weekStart + 7) {
19039             dow.cn.push({
19040                 tag: 'th',
19041                 cls: 'dow',
19042                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19043             });
19044         }
19045         
19046         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19047     },
19048     
19049     fillMonths: function()
19050     {    
19051         var i = 0;
19052         var months = this.picker().select('>.datepicker-months td', true).first();
19053         
19054         months.dom.innerHTML = '';
19055         
19056         while (i < 12) {
19057             var month = {
19058                 tag: 'span',
19059                 cls: 'month',
19060                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19061             };
19062             
19063             months.createChild(month);
19064         }
19065         
19066     },
19067     
19068     update: function()
19069     {
19070         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;
19071         
19072         if (this.date < this.startDate) {
19073             this.viewDate = new Date(this.startDate);
19074         } else if (this.date > this.endDate) {
19075             this.viewDate = new Date(this.endDate);
19076         } else {
19077             this.viewDate = new Date(this.date);
19078         }
19079         
19080         this.fill();
19081     },
19082     
19083     fill: function() 
19084     {
19085         var d = new Date(this.viewDate),
19086                 year = d.getUTCFullYear(),
19087                 month = d.getUTCMonth(),
19088                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19089                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19090                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19091                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19092                 currentDate = this.date && this.date.valueOf(),
19093                 today = this.UTCToday();
19094         
19095         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19096         
19097 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19098         
19099 //        this.picker.select('>tfoot th.today').
19100 //                                              .text(dates[this.language].today)
19101 //                                              .toggle(this.todayBtn !== false);
19102     
19103         this.updateNavArrows();
19104         this.fillMonths();
19105                                                 
19106         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19107         
19108         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19109          
19110         prevMonth.setUTCDate(day);
19111         
19112         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19113         
19114         var nextMonth = new Date(prevMonth);
19115         
19116         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19117         
19118         nextMonth = nextMonth.valueOf();
19119         
19120         var fillMonths = false;
19121         
19122         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19123         
19124         while(prevMonth.valueOf() <= nextMonth) {
19125             var clsName = '';
19126             
19127             if (prevMonth.getUTCDay() === this.weekStart) {
19128                 if(fillMonths){
19129                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19130                 }
19131                     
19132                 fillMonths = {
19133                     tag: 'tr',
19134                     cn: []
19135                 };
19136                 
19137                 if(this.calendarWeeks){
19138                     // ISO 8601: First week contains first thursday.
19139                     // ISO also states week starts on Monday, but we can be more abstract here.
19140                     var
19141                     // Start of current week: based on weekstart/current date
19142                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19143                     // Thursday of this week
19144                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19145                     // First Thursday of year, year from thursday
19146                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19147                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19148                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19149                     
19150                     fillMonths.cn.push({
19151                         tag: 'td',
19152                         cls: 'cw',
19153                         html: calWeek
19154                     });
19155                 }
19156             }
19157             
19158             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19159                 clsName += ' old';
19160             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19161                 clsName += ' new';
19162             }
19163             if (this.todayHighlight &&
19164                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19165                 prevMonth.getUTCMonth() == today.getMonth() &&
19166                 prevMonth.getUTCDate() == today.getDate()) {
19167                 clsName += ' today';
19168             }
19169             
19170             if (currentDate && prevMonth.valueOf() === currentDate) {
19171                 clsName += ' active';
19172             }
19173             
19174             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19175                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19176                     clsName += ' disabled';
19177             }
19178             
19179             fillMonths.cn.push({
19180                 tag: 'td',
19181                 cls: 'day ' + clsName,
19182                 html: prevMonth.getDate()
19183             });
19184             
19185             prevMonth.setDate(prevMonth.getDate()+1);
19186         }
19187           
19188         var currentYear = this.date && this.date.getUTCFullYear();
19189         var currentMonth = this.date && this.date.getUTCMonth();
19190         
19191         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19192         
19193         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19194             v.removeClass('active');
19195             
19196             if(currentYear === year && k === currentMonth){
19197                 v.addClass('active');
19198             }
19199             
19200             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19201                 v.addClass('disabled');
19202             }
19203             
19204         });
19205         
19206         
19207         year = parseInt(year/10, 10) * 10;
19208         
19209         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19210         
19211         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19212         
19213         year -= 1;
19214         for (var i = -1; i < 11; i++) {
19215             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19216                 tag: 'span',
19217                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19218                 html: year
19219             });
19220             
19221             year += 1;
19222         }
19223     },
19224     
19225     showMode: function(dir) 
19226     {
19227         if (dir) {
19228             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19229         }
19230         
19231         Roo.each(this.picker().select('>div',true).elements, function(v){
19232             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19233             v.hide();
19234         });
19235         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19236     },
19237     
19238     place: function()
19239     {
19240         if(this.isInline) {
19241             return;
19242         }
19243         
19244         this.picker().removeClass(['bottom', 'top']);
19245         
19246         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19247             /*
19248              * place to the top of element!
19249              *
19250              */
19251             
19252             this.picker().addClass('top');
19253             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19254             
19255             return;
19256         }
19257         
19258         this.picker().addClass('bottom');
19259         
19260         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19261     },
19262     
19263     parseDate : function(value)
19264     {
19265         if(!value || value instanceof Date){
19266             return value;
19267         }
19268         var v = Date.parseDate(value, this.format);
19269         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19270             v = Date.parseDate(value, 'Y-m-d');
19271         }
19272         if(!v && this.altFormats){
19273             if(!this.altFormatsArray){
19274                 this.altFormatsArray = this.altFormats.split("|");
19275             }
19276             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19277                 v = Date.parseDate(value, this.altFormatsArray[i]);
19278             }
19279         }
19280         return v;
19281     },
19282     
19283     formatDate : function(date, fmt)
19284     {   
19285         return (!date || !(date instanceof Date)) ?
19286         date : date.dateFormat(fmt || this.format);
19287     },
19288     
19289     onFocus : function()
19290     {
19291         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19292         this.showPopup();
19293     },
19294     
19295     onBlur : function()
19296     {
19297         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19298         
19299         var d = this.inputEl().getValue();
19300         
19301         this.setValue(d);
19302                 
19303         this.hidePopup();
19304     },
19305     
19306     showPopup : function()
19307     {
19308         this.picker().show();
19309         this.update();
19310         this.place();
19311         
19312         this.fireEvent('showpopup', this, this.date);
19313     },
19314     
19315     hidePopup : function()
19316     {
19317         if(this.isInline) {
19318             return;
19319         }
19320         this.picker().hide();
19321         this.viewMode = this.startViewMode;
19322         this.showMode();
19323         
19324         this.fireEvent('hidepopup', this, this.date);
19325         
19326     },
19327     
19328     onMousedown: function(e)
19329     {
19330         e.stopPropagation();
19331         e.preventDefault();
19332     },
19333     
19334     keyup: function(e)
19335     {
19336         Roo.bootstrap.DateField.superclass.keyup.call(this);
19337         this.update();
19338     },
19339
19340     setValue: function(v)
19341     {
19342         if(this.fireEvent('beforeselect', this, v) !== false){
19343             var d = new Date(this.parseDate(v) ).clearTime();
19344         
19345             if(isNaN(d.getTime())){
19346                 this.date = this.viewDate = '';
19347                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19348                 return;
19349             }
19350
19351             v = this.formatDate(d);
19352
19353             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19354
19355             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19356
19357             this.update();
19358
19359             this.fireEvent('select', this, this.date);
19360         }
19361     },
19362     
19363     getValue: function()
19364     {
19365         return this.formatDate(this.date);
19366     },
19367     
19368     fireKey: function(e)
19369     {
19370         if (!this.picker().isVisible()){
19371             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19372                 this.showPopup();
19373             }
19374             return;
19375         }
19376         
19377         var dateChanged = false,
19378         dir, day, month,
19379         newDate, newViewDate;
19380         
19381         switch(e.keyCode){
19382             case 27: // escape
19383                 this.hidePopup();
19384                 e.preventDefault();
19385                 break;
19386             case 37: // left
19387             case 39: // right
19388                 if (!this.keyboardNavigation) {
19389                     break;
19390                 }
19391                 dir = e.keyCode == 37 ? -1 : 1;
19392                 
19393                 if (e.ctrlKey){
19394                     newDate = this.moveYear(this.date, dir);
19395                     newViewDate = this.moveYear(this.viewDate, dir);
19396                 } else if (e.shiftKey){
19397                     newDate = this.moveMonth(this.date, dir);
19398                     newViewDate = this.moveMonth(this.viewDate, dir);
19399                 } else {
19400                     newDate = new Date(this.date);
19401                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19402                     newViewDate = new Date(this.viewDate);
19403                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19404                 }
19405                 if (this.dateWithinRange(newDate)){
19406                     this.date = newDate;
19407                     this.viewDate = newViewDate;
19408                     this.setValue(this.formatDate(this.date));
19409 //                    this.update();
19410                     e.preventDefault();
19411                     dateChanged = true;
19412                 }
19413                 break;
19414             case 38: // up
19415             case 40: // down
19416                 if (!this.keyboardNavigation) {
19417                     break;
19418                 }
19419                 dir = e.keyCode == 38 ? -1 : 1;
19420                 if (e.ctrlKey){
19421                     newDate = this.moveYear(this.date, dir);
19422                     newViewDate = this.moveYear(this.viewDate, dir);
19423                 } else if (e.shiftKey){
19424                     newDate = this.moveMonth(this.date, dir);
19425                     newViewDate = this.moveMonth(this.viewDate, dir);
19426                 } else {
19427                     newDate = new Date(this.date);
19428                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19429                     newViewDate = new Date(this.viewDate);
19430                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19431                 }
19432                 if (this.dateWithinRange(newDate)){
19433                     this.date = newDate;
19434                     this.viewDate = newViewDate;
19435                     this.setValue(this.formatDate(this.date));
19436 //                    this.update();
19437                     e.preventDefault();
19438                     dateChanged = true;
19439                 }
19440                 break;
19441             case 13: // enter
19442                 this.setValue(this.formatDate(this.date));
19443                 this.hidePopup();
19444                 e.preventDefault();
19445                 break;
19446             case 9: // tab
19447                 this.setValue(this.formatDate(this.date));
19448                 this.hidePopup();
19449                 break;
19450             case 16: // shift
19451             case 17: // ctrl
19452             case 18: // alt
19453                 break;
19454             default :
19455                 this.hidePopup();
19456                 
19457         }
19458     },
19459     
19460     
19461     onClick: function(e) 
19462     {
19463         e.stopPropagation();
19464         e.preventDefault();
19465         
19466         var target = e.getTarget();
19467         
19468         if(target.nodeName.toLowerCase() === 'i'){
19469             target = Roo.get(target).dom.parentNode;
19470         }
19471         
19472         var nodeName = target.nodeName;
19473         var className = target.className;
19474         var html = target.innerHTML;
19475         //Roo.log(nodeName);
19476         
19477         switch(nodeName.toLowerCase()) {
19478             case 'th':
19479                 switch(className) {
19480                     case 'switch':
19481                         this.showMode(1);
19482                         break;
19483                     case 'prev':
19484                     case 'next':
19485                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19486                         switch(this.viewMode){
19487                                 case 0:
19488                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19489                                         break;
19490                                 case 1:
19491                                 case 2:
19492                                         this.viewDate = this.moveYear(this.viewDate, dir);
19493                                         break;
19494                         }
19495                         this.fill();
19496                         break;
19497                     case 'today':
19498                         var date = new Date();
19499                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19500 //                        this.fill()
19501                         this.setValue(this.formatDate(this.date));
19502                         
19503                         this.hidePopup();
19504                         break;
19505                 }
19506                 break;
19507             case 'span':
19508                 if (className.indexOf('disabled') < 0) {
19509                     this.viewDate.setUTCDate(1);
19510                     if (className.indexOf('month') > -1) {
19511                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19512                     } else {
19513                         var year = parseInt(html, 10) || 0;
19514                         this.viewDate.setUTCFullYear(year);
19515                         
19516                     }
19517                     
19518                     if(this.singleMode){
19519                         this.setValue(this.formatDate(this.viewDate));
19520                         this.hidePopup();
19521                         return;
19522                     }
19523                     
19524                     this.showMode(-1);
19525                     this.fill();
19526                 }
19527                 break;
19528                 
19529             case 'td':
19530                 //Roo.log(className);
19531                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19532                     var day = parseInt(html, 10) || 1;
19533                     var year = this.viewDate.getUTCFullYear(),
19534                         month = this.viewDate.getUTCMonth();
19535
19536                     if (className.indexOf('old') > -1) {
19537                         if(month === 0 ){
19538                             month = 11;
19539                             year -= 1;
19540                         }else{
19541                             month -= 1;
19542                         }
19543                     } else if (className.indexOf('new') > -1) {
19544                         if (month == 11) {
19545                             month = 0;
19546                             year += 1;
19547                         } else {
19548                             month += 1;
19549                         }
19550                     }
19551                     //Roo.log([year,month,day]);
19552                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19553                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19554 //                    this.fill();
19555                     //Roo.log(this.formatDate(this.date));
19556                     this.setValue(this.formatDate(this.date));
19557                     this.hidePopup();
19558                 }
19559                 break;
19560         }
19561     },
19562     
19563     setStartDate: function(startDate)
19564     {
19565         this.startDate = startDate || -Infinity;
19566         if (this.startDate !== -Infinity) {
19567             this.startDate = this.parseDate(this.startDate);
19568         }
19569         this.update();
19570         this.updateNavArrows();
19571     },
19572
19573     setEndDate: function(endDate)
19574     {
19575         this.endDate = endDate || Infinity;
19576         if (this.endDate !== Infinity) {
19577             this.endDate = this.parseDate(this.endDate);
19578         }
19579         this.update();
19580         this.updateNavArrows();
19581     },
19582     
19583     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19584     {
19585         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19586         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19587             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19588         }
19589         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19590             return parseInt(d, 10);
19591         });
19592         this.update();
19593         this.updateNavArrows();
19594     },
19595     
19596     updateNavArrows: function() 
19597     {
19598         if(this.singleMode){
19599             return;
19600         }
19601         
19602         var d = new Date(this.viewDate),
19603         year = d.getUTCFullYear(),
19604         month = d.getUTCMonth();
19605         
19606         Roo.each(this.picker().select('.prev', true).elements, function(v){
19607             v.show();
19608             switch (this.viewMode) {
19609                 case 0:
19610
19611                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19612                         v.hide();
19613                     }
19614                     break;
19615                 case 1:
19616                 case 2:
19617                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19618                         v.hide();
19619                     }
19620                     break;
19621             }
19622         });
19623         
19624         Roo.each(this.picker().select('.next', true).elements, function(v){
19625             v.show();
19626             switch (this.viewMode) {
19627                 case 0:
19628
19629                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19630                         v.hide();
19631                     }
19632                     break;
19633                 case 1:
19634                 case 2:
19635                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19636                         v.hide();
19637                     }
19638                     break;
19639             }
19640         })
19641     },
19642     
19643     moveMonth: function(date, dir)
19644     {
19645         if (!dir) {
19646             return date;
19647         }
19648         var new_date = new Date(date.valueOf()),
19649         day = new_date.getUTCDate(),
19650         month = new_date.getUTCMonth(),
19651         mag = Math.abs(dir),
19652         new_month, test;
19653         dir = dir > 0 ? 1 : -1;
19654         if (mag == 1){
19655             test = dir == -1
19656             // If going back one month, make sure month is not current month
19657             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19658             ? function(){
19659                 return new_date.getUTCMonth() == month;
19660             }
19661             // If going forward one month, make sure month is as expected
19662             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19663             : function(){
19664                 return new_date.getUTCMonth() != new_month;
19665             };
19666             new_month = month + dir;
19667             new_date.setUTCMonth(new_month);
19668             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19669             if (new_month < 0 || new_month > 11) {
19670                 new_month = (new_month + 12) % 12;
19671             }
19672         } else {
19673             // For magnitudes >1, move one month at a time...
19674             for (var i=0; i<mag; i++) {
19675                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19676                 new_date = this.moveMonth(new_date, dir);
19677             }
19678             // ...then reset the day, keeping it in the new month
19679             new_month = new_date.getUTCMonth();
19680             new_date.setUTCDate(day);
19681             test = function(){
19682                 return new_month != new_date.getUTCMonth();
19683             };
19684         }
19685         // Common date-resetting loop -- if date is beyond end of month, make it
19686         // end of month
19687         while (test()){
19688             new_date.setUTCDate(--day);
19689             new_date.setUTCMonth(new_month);
19690         }
19691         return new_date;
19692     },
19693
19694     moveYear: function(date, dir)
19695     {
19696         return this.moveMonth(date, dir*12);
19697     },
19698
19699     dateWithinRange: function(date)
19700     {
19701         return date >= this.startDate && date <= this.endDate;
19702     },
19703
19704     
19705     remove: function() 
19706     {
19707         this.picker().remove();
19708     },
19709     
19710     validateValue : function(value)
19711     {
19712         if(this.getVisibilityEl().hasClass('hidden')){
19713             return true;
19714         }
19715         
19716         if(value.length < 1)  {
19717             if(this.allowBlank){
19718                 return true;
19719             }
19720             return false;
19721         }
19722         
19723         if(value.length < this.minLength){
19724             return false;
19725         }
19726         if(value.length > this.maxLength){
19727             return false;
19728         }
19729         if(this.vtype){
19730             var vt = Roo.form.VTypes;
19731             if(!vt[this.vtype](value, this)){
19732                 return false;
19733             }
19734         }
19735         if(typeof this.validator == "function"){
19736             var msg = this.validator(value);
19737             if(msg !== true){
19738                 return false;
19739             }
19740         }
19741         
19742         if(this.regex && !this.regex.test(value)){
19743             return false;
19744         }
19745         
19746         if(typeof(this.parseDate(value)) == 'undefined'){
19747             return false;
19748         }
19749         
19750         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19751             return false;
19752         }      
19753         
19754         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19755             return false;
19756         } 
19757         
19758         
19759         return true;
19760     },
19761     
19762     reset : function()
19763     {
19764         this.date = this.viewDate = '';
19765         
19766         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19767     }
19768    
19769 });
19770
19771 Roo.apply(Roo.bootstrap.DateField,  {
19772     
19773     head : {
19774         tag: 'thead',
19775         cn: [
19776         {
19777             tag: 'tr',
19778             cn: [
19779             {
19780                 tag: 'th',
19781                 cls: 'prev',
19782                 html: '<i class="fa fa-arrow-left"/>'
19783             },
19784             {
19785                 tag: 'th',
19786                 cls: 'switch',
19787                 colspan: '5'
19788             },
19789             {
19790                 tag: 'th',
19791                 cls: 'next',
19792                 html: '<i class="fa fa-arrow-right"/>'
19793             }
19794
19795             ]
19796         }
19797         ]
19798     },
19799     
19800     content : {
19801         tag: 'tbody',
19802         cn: [
19803         {
19804             tag: 'tr',
19805             cn: [
19806             {
19807                 tag: 'td',
19808                 colspan: '7'
19809             }
19810             ]
19811         }
19812         ]
19813     },
19814     
19815     footer : {
19816         tag: 'tfoot',
19817         cn: [
19818         {
19819             tag: 'tr',
19820             cn: [
19821             {
19822                 tag: 'th',
19823                 colspan: '7',
19824                 cls: 'today'
19825             }
19826                     
19827             ]
19828         }
19829         ]
19830     },
19831     
19832     dates:{
19833         en: {
19834             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19835             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19836             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19837             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19838             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19839             today: "Today"
19840         }
19841     },
19842     
19843     modes: [
19844     {
19845         clsName: 'days',
19846         navFnc: 'Month',
19847         navStep: 1
19848     },
19849     {
19850         clsName: 'months',
19851         navFnc: 'FullYear',
19852         navStep: 1
19853     },
19854     {
19855         clsName: 'years',
19856         navFnc: 'FullYear',
19857         navStep: 10
19858     }]
19859 });
19860
19861 Roo.apply(Roo.bootstrap.DateField,  {
19862   
19863     template : {
19864         tag: 'div',
19865         cls: 'datepicker dropdown-menu roo-dynamic',
19866         cn: [
19867         {
19868             tag: 'div',
19869             cls: 'datepicker-days',
19870             cn: [
19871             {
19872                 tag: 'table',
19873                 cls: 'table-condensed',
19874                 cn:[
19875                 Roo.bootstrap.DateField.head,
19876                 {
19877                     tag: 'tbody'
19878                 },
19879                 Roo.bootstrap.DateField.footer
19880                 ]
19881             }
19882             ]
19883         },
19884         {
19885             tag: 'div',
19886             cls: 'datepicker-months',
19887             cn: [
19888             {
19889                 tag: 'table',
19890                 cls: 'table-condensed',
19891                 cn:[
19892                 Roo.bootstrap.DateField.head,
19893                 Roo.bootstrap.DateField.content,
19894                 Roo.bootstrap.DateField.footer
19895                 ]
19896             }
19897             ]
19898         },
19899         {
19900             tag: 'div',
19901             cls: 'datepicker-years',
19902             cn: [
19903             {
19904                 tag: 'table',
19905                 cls: 'table-condensed',
19906                 cn:[
19907                 Roo.bootstrap.DateField.head,
19908                 Roo.bootstrap.DateField.content,
19909                 Roo.bootstrap.DateField.footer
19910                 ]
19911             }
19912             ]
19913         }
19914         ]
19915     }
19916 });
19917
19918  
19919
19920  /*
19921  * - LGPL
19922  *
19923  * TimeField
19924  * 
19925  */
19926
19927 /**
19928  * @class Roo.bootstrap.TimeField
19929  * @extends Roo.bootstrap.Input
19930  * Bootstrap DateField class
19931  * 
19932  * 
19933  * @constructor
19934  * Create a new TimeField
19935  * @param {Object} config The config object
19936  */
19937
19938 Roo.bootstrap.TimeField = function(config){
19939     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19940     this.addEvents({
19941             /**
19942              * @event show
19943              * Fires when this field show.
19944              * @param {Roo.bootstrap.DateField} thisthis
19945              * @param {Mixed} date The date value
19946              */
19947             show : true,
19948             /**
19949              * @event show
19950              * Fires when this field hide.
19951              * @param {Roo.bootstrap.DateField} this
19952              * @param {Mixed} date The date value
19953              */
19954             hide : true,
19955             /**
19956              * @event select
19957              * Fires when select a date.
19958              * @param {Roo.bootstrap.DateField} this
19959              * @param {Mixed} date The date value
19960              */
19961             select : true
19962         });
19963 };
19964
19965 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19966     
19967     /**
19968      * @cfg {String} format
19969      * The default time format string which can be overriden for localization support.  The format must be
19970      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19971      */
19972     format : "H:i",
19973        
19974     onRender: function(ct, position)
19975     {
19976         
19977         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19978                 
19979         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19980         
19981         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19982         
19983         this.pop = this.picker().select('>.datepicker-time',true).first();
19984         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19985         
19986         this.picker().on('mousedown', this.onMousedown, this);
19987         this.picker().on('click', this.onClick, this);
19988         
19989         this.picker().addClass('datepicker-dropdown');
19990     
19991         this.fillTime();
19992         this.update();
19993             
19994         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19995         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19996         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19997         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19998         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19999         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20000
20001     },
20002     
20003     fireKey: function(e){
20004         if (!this.picker().isVisible()){
20005             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20006                 this.show();
20007             }
20008             return;
20009         }
20010
20011         e.preventDefault();
20012         
20013         switch(e.keyCode){
20014             case 27: // escape
20015                 this.hide();
20016                 break;
20017             case 37: // left
20018             case 39: // right
20019                 this.onTogglePeriod();
20020                 break;
20021             case 38: // up
20022                 this.onIncrementMinutes();
20023                 break;
20024             case 40: // down
20025                 this.onDecrementMinutes();
20026                 break;
20027             case 13: // enter
20028             case 9: // tab
20029                 this.setTime();
20030                 break;
20031         }
20032     },
20033     
20034     onClick: function(e) {
20035         e.stopPropagation();
20036         e.preventDefault();
20037     },
20038     
20039     picker : function()
20040     {
20041         return this.el.select('.datepicker', true).first();
20042     },
20043     
20044     fillTime: function()
20045     {    
20046         var time = this.pop.select('tbody', true).first();
20047         
20048         time.dom.innerHTML = '';
20049         
20050         time.createChild({
20051             tag: 'tr',
20052             cn: [
20053                 {
20054                     tag: 'td',
20055                     cn: [
20056                         {
20057                             tag: 'a',
20058                             href: '#',
20059                             cls: 'btn',
20060                             cn: [
20061                                 {
20062                                     tag: 'span',
20063                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20064                                 }
20065                             ]
20066                         } 
20067                     ]
20068                 },
20069                 {
20070                     tag: 'td',
20071                     cls: 'separator'
20072                 },
20073                 {
20074                     tag: 'td',
20075                     cn: [
20076                         {
20077                             tag: 'a',
20078                             href: '#',
20079                             cls: 'btn',
20080                             cn: [
20081                                 {
20082                                     tag: 'span',
20083                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20084                                 }
20085                             ]
20086                         }
20087                     ]
20088                 },
20089                 {
20090                     tag: 'td',
20091                     cls: 'separator'
20092                 }
20093             ]
20094         });
20095         
20096         time.createChild({
20097             tag: 'tr',
20098             cn: [
20099                 {
20100                     tag: 'td',
20101                     cn: [
20102                         {
20103                             tag: 'span',
20104                             cls: 'timepicker-hour',
20105                             html: '00'
20106                         }  
20107                     ]
20108                 },
20109                 {
20110                     tag: 'td',
20111                     cls: 'separator',
20112                     html: ':'
20113                 },
20114                 {
20115                     tag: 'td',
20116                     cn: [
20117                         {
20118                             tag: 'span',
20119                             cls: 'timepicker-minute',
20120                             html: '00'
20121                         }  
20122                     ]
20123                 },
20124                 {
20125                     tag: 'td',
20126                     cls: 'separator'
20127                 },
20128                 {
20129                     tag: 'td',
20130                     cn: [
20131                         {
20132                             tag: 'button',
20133                             type: 'button',
20134                             cls: 'btn btn-primary period',
20135                             html: 'AM'
20136                             
20137                         }
20138                     ]
20139                 }
20140             ]
20141         });
20142         
20143         time.createChild({
20144             tag: 'tr',
20145             cn: [
20146                 {
20147                     tag: 'td',
20148                     cn: [
20149                         {
20150                             tag: 'a',
20151                             href: '#',
20152                             cls: 'btn',
20153                             cn: [
20154                                 {
20155                                     tag: 'span',
20156                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20157                                 }
20158                             ]
20159                         }
20160                     ]
20161                 },
20162                 {
20163                     tag: 'td',
20164                     cls: 'separator'
20165                 },
20166                 {
20167                     tag: 'td',
20168                     cn: [
20169                         {
20170                             tag: 'a',
20171                             href: '#',
20172                             cls: 'btn',
20173                             cn: [
20174                                 {
20175                                     tag: 'span',
20176                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20177                                 }
20178                             ]
20179                         }
20180                     ]
20181                 },
20182                 {
20183                     tag: 'td',
20184                     cls: 'separator'
20185                 }
20186             ]
20187         });
20188         
20189     },
20190     
20191     update: function()
20192     {
20193         
20194         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20195         
20196         this.fill();
20197     },
20198     
20199     fill: function() 
20200     {
20201         var hours = this.time.getHours();
20202         var minutes = this.time.getMinutes();
20203         var period = 'AM';
20204         
20205         if(hours > 11){
20206             period = 'PM';
20207         }
20208         
20209         if(hours == 0){
20210             hours = 12;
20211         }
20212         
20213         
20214         if(hours > 12){
20215             hours = hours - 12;
20216         }
20217         
20218         if(hours < 10){
20219             hours = '0' + hours;
20220         }
20221         
20222         if(minutes < 10){
20223             minutes = '0' + minutes;
20224         }
20225         
20226         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20227         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20228         this.pop.select('button', true).first().dom.innerHTML = period;
20229         
20230     },
20231     
20232     place: function()
20233     {   
20234         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20235         
20236         var cls = ['bottom'];
20237         
20238         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20239             cls.pop();
20240             cls.push('top');
20241         }
20242         
20243         cls.push('right');
20244         
20245         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20246             cls.pop();
20247             cls.push('left');
20248         }
20249         
20250         this.picker().addClass(cls.join('-'));
20251         
20252         var _this = this;
20253         
20254         Roo.each(cls, function(c){
20255             if(c == 'bottom'){
20256                 _this.picker().setTop(_this.inputEl().getHeight());
20257                 return;
20258             }
20259             if(c == 'top'){
20260                 _this.picker().setTop(0 - _this.picker().getHeight());
20261                 return;
20262             }
20263             
20264             if(c == 'left'){
20265                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20266                 return;
20267             }
20268             if(c == 'right'){
20269                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20270                 return;
20271             }
20272         });
20273         
20274     },
20275   
20276     onFocus : function()
20277     {
20278         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20279         this.show();
20280     },
20281     
20282     onBlur : function()
20283     {
20284         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20285         this.hide();
20286     },
20287     
20288     show : function()
20289     {
20290         this.picker().show();
20291         this.pop.show();
20292         this.update();
20293         this.place();
20294         
20295         this.fireEvent('show', this, this.date);
20296     },
20297     
20298     hide : function()
20299     {
20300         this.picker().hide();
20301         this.pop.hide();
20302         
20303         this.fireEvent('hide', this, this.date);
20304     },
20305     
20306     setTime : function()
20307     {
20308         this.hide();
20309         this.setValue(this.time.format(this.format));
20310         
20311         this.fireEvent('select', this, this.date);
20312         
20313         
20314     },
20315     
20316     onMousedown: function(e){
20317         e.stopPropagation();
20318         e.preventDefault();
20319     },
20320     
20321     onIncrementHours: function()
20322     {
20323         Roo.log('onIncrementHours');
20324         this.time = this.time.add(Date.HOUR, 1);
20325         this.update();
20326         
20327     },
20328     
20329     onDecrementHours: function()
20330     {
20331         Roo.log('onDecrementHours');
20332         this.time = this.time.add(Date.HOUR, -1);
20333         this.update();
20334     },
20335     
20336     onIncrementMinutes: function()
20337     {
20338         Roo.log('onIncrementMinutes');
20339         this.time = this.time.add(Date.MINUTE, 1);
20340         this.update();
20341     },
20342     
20343     onDecrementMinutes: function()
20344     {
20345         Roo.log('onDecrementMinutes');
20346         this.time = this.time.add(Date.MINUTE, -1);
20347         this.update();
20348     },
20349     
20350     onTogglePeriod: function()
20351     {
20352         Roo.log('onTogglePeriod');
20353         this.time = this.time.add(Date.HOUR, 12);
20354         this.update();
20355     }
20356     
20357    
20358 });
20359
20360 Roo.apply(Roo.bootstrap.TimeField,  {
20361     
20362     content : {
20363         tag: 'tbody',
20364         cn: [
20365             {
20366                 tag: 'tr',
20367                 cn: [
20368                 {
20369                     tag: 'td',
20370                     colspan: '7'
20371                 }
20372                 ]
20373             }
20374         ]
20375     },
20376     
20377     footer : {
20378         tag: 'tfoot',
20379         cn: [
20380             {
20381                 tag: 'tr',
20382                 cn: [
20383                 {
20384                     tag: 'th',
20385                     colspan: '7',
20386                     cls: '',
20387                     cn: [
20388                         {
20389                             tag: 'button',
20390                             cls: 'btn btn-info ok',
20391                             html: 'OK'
20392                         }
20393                     ]
20394                 }
20395
20396                 ]
20397             }
20398         ]
20399     }
20400 });
20401
20402 Roo.apply(Roo.bootstrap.TimeField,  {
20403   
20404     template : {
20405         tag: 'div',
20406         cls: 'datepicker dropdown-menu',
20407         cn: [
20408             {
20409                 tag: 'div',
20410                 cls: 'datepicker-time',
20411                 cn: [
20412                 {
20413                     tag: 'table',
20414                     cls: 'table-condensed',
20415                     cn:[
20416                     Roo.bootstrap.TimeField.content,
20417                     Roo.bootstrap.TimeField.footer
20418                     ]
20419                 }
20420                 ]
20421             }
20422         ]
20423     }
20424 });
20425
20426  
20427
20428  /*
20429  * - LGPL
20430  *
20431  * MonthField
20432  * 
20433  */
20434
20435 /**
20436  * @class Roo.bootstrap.MonthField
20437  * @extends Roo.bootstrap.Input
20438  * Bootstrap MonthField class
20439  * 
20440  * @cfg {String} language default en
20441  * 
20442  * @constructor
20443  * Create a new MonthField
20444  * @param {Object} config The config object
20445  */
20446
20447 Roo.bootstrap.MonthField = function(config){
20448     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20449     
20450     this.addEvents({
20451         /**
20452          * @event show
20453          * Fires when this field show.
20454          * @param {Roo.bootstrap.MonthField} this
20455          * @param {Mixed} date The date value
20456          */
20457         show : true,
20458         /**
20459          * @event show
20460          * Fires when this field hide.
20461          * @param {Roo.bootstrap.MonthField} this
20462          * @param {Mixed} date The date value
20463          */
20464         hide : true,
20465         /**
20466          * @event select
20467          * Fires when select a date.
20468          * @param {Roo.bootstrap.MonthField} this
20469          * @param {String} oldvalue The old value
20470          * @param {String} newvalue The new value
20471          */
20472         select : true
20473     });
20474 };
20475
20476 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20477     
20478     onRender: function(ct, position)
20479     {
20480         
20481         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20482         
20483         this.language = this.language || 'en';
20484         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20485         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20486         
20487         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20488         this.isInline = false;
20489         this.isInput = true;
20490         this.component = this.el.select('.add-on', true).first() || false;
20491         this.component = (this.component && this.component.length === 0) ? false : this.component;
20492         this.hasInput = this.component && this.inputEL().length;
20493         
20494         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20495         
20496         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20497         
20498         this.picker().on('mousedown', this.onMousedown, this);
20499         this.picker().on('click', this.onClick, this);
20500         
20501         this.picker().addClass('datepicker-dropdown');
20502         
20503         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20504             v.setStyle('width', '189px');
20505         });
20506         
20507         this.fillMonths();
20508         
20509         this.update();
20510         
20511         if(this.isInline) {
20512             this.show();
20513         }
20514         
20515     },
20516     
20517     setValue: function(v, suppressEvent)
20518     {   
20519         var o = this.getValue();
20520         
20521         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20522         
20523         this.update();
20524
20525         if(suppressEvent !== true){
20526             this.fireEvent('select', this, o, v);
20527         }
20528         
20529     },
20530     
20531     getValue: function()
20532     {
20533         return this.value;
20534     },
20535     
20536     onClick: function(e) 
20537     {
20538         e.stopPropagation();
20539         e.preventDefault();
20540         
20541         var target = e.getTarget();
20542         
20543         if(target.nodeName.toLowerCase() === 'i'){
20544             target = Roo.get(target).dom.parentNode;
20545         }
20546         
20547         var nodeName = target.nodeName;
20548         var className = target.className;
20549         var html = target.innerHTML;
20550         
20551         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20552             return;
20553         }
20554         
20555         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20556         
20557         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20558         
20559         this.hide();
20560                         
20561     },
20562     
20563     picker : function()
20564     {
20565         return this.pickerEl;
20566     },
20567     
20568     fillMonths: function()
20569     {    
20570         var i = 0;
20571         var months = this.picker().select('>.datepicker-months td', true).first();
20572         
20573         months.dom.innerHTML = '';
20574         
20575         while (i < 12) {
20576             var month = {
20577                 tag: 'span',
20578                 cls: 'month',
20579                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20580             };
20581             
20582             months.createChild(month);
20583         }
20584         
20585     },
20586     
20587     update: function()
20588     {
20589         var _this = this;
20590         
20591         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20592             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20593         }
20594         
20595         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20596             e.removeClass('active');
20597             
20598             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20599                 e.addClass('active');
20600             }
20601         })
20602     },
20603     
20604     place: function()
20605     {
20606         if(this.isInline) {
20607             return;
20608         }
20609         
20610         this.picker().removeClass(['bottom', 'top']);
20611         
20612         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20613             /*
20614              * place to the top of element!
20615              *
20616              */
20617             
20618             this.picker().addClass('top');
20619             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20620             
20621             return;
20622         }
20623         
20624         this.picker().addClass('bottom');
20625         
20626         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20627     },
20628     
20629     onFocus : function()
20630     {
20631         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20632         this.show();
20633     },
20634     
20635     onBlur : function()
20636     {
20637         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20638         
20639         var d = this.inputEl().getValue();
20640         
20641         this.setValue(d);
20642                 
20643         this.hide();
20644     },
20645     
20646     show : function()
20647     {
20648         this.picker().show();
20649         this.picker().select('>.datepicker-months', true).first().show();
20650         this.update();
20651         this.place();
20652         
20653         this.fireEvent('show', this, this.date);
20654     },
20655     
20656     hide : function()
20657     {
20658         if(this.isInline) {
20659             return;
20660         }
20661         this.picker().hide();
20662         this.fireEvent('hide', this, this.date);
20663         
20664     },
20665     
20666     onMousedown: function(e)
20667     {
20668         e.stopPropagation();
20669         e.preventDefault();
20670     },
20671     
20672     keyup: function(e)
20673     {
20674         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20675         this.update();
20676     },
20677
20678     fireKey: function(e)
20679     {
20680         if (!this.picker().isVisible()){
20681             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20682                 this.show();
20683             }
20684             return;
20685         }
20686         
20687         var dir;
20688         
20689         switch(e.keyCode){
20690             case 27: // escape
20691                 this.hide();
20692                 e.preventDefault();
20693                 break;
20694             case 37: // left
20695             case 39: // right
20696                 dir = e.keyCode == 37 ? -1 : 1;
20697                 
20698                 this.vIndex = this.vIndex + dir;
20699                 
20700                 if(this.vIndex < 0){
20701                     this.vIndex = 0;
20702                 }
20703                 
20704                 if(this.vIndex > 11){
20705                     this.vIndex = 11;
20706                 }
20707                 
20708                 if(isNaN(this.vIndex)){
20709                     this.vIndex = 0;
20710                 }
20711                 
20712                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20713                 
20714                 break;
20715             case 38: // up
20716             case 40: // down
20717                 
20718                 dir = e.keyCode == 38 ? -1 : 1;
20719                 
20720                 this.vIndex = this.vIndex + dir * 4;
20721                 
20722                 if(this.vIndex < 0){
20723                     this.vIndex = 0;
20724                 }
20725                 
20726                 if(this.vIndex > 11){
20727                     this.vIndex = 11;
20728                 }
20729                 
20730                 if(isNaN(this.vIndex)){
20731                     this.vIndex = 0;
20732                 }
20733                 
20734                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20735                 break;
20736                 
20737             case 13: // enter
20738                 
20739                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20740                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20741                 }
20742                 
20743                 this.hide();
20744                 e.preventDefault();
20745                 break;
20746             case 9: // tab
20747                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20748                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20749                 }
20750                 this.hide();
20751                 break;
20752             case 16: // shift
20753             case 17: // ctrl
20754             case 18: // alt
20755                 break;
20756             default :
20757                 this.hide();
20758                 
20759         }
20760     },
20761     
20762     remove: function() 
20763     {
20764         this.picker().remove();
20765     }
20766    
20767 });
20768
20769 Roo.apply(Roo.bootstrap.MonthField,  {
20770     
20771     content : {
20772         tag: 'tbody',
20773         cn: [
20774         {
20775             tag: 'tr',
20776             cn: [
20777             {
20778                 tag: 'td',
20779                 colspan: '7'
20780             }
20781             ]
20782         }
20783         ]
20784     },
20785     
20786     dates:{
20787         en: {
20788             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20789             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20790         }
20791     }
20792 });
20793
20794 Roo.apply(Roo.bootstrap.MonthField,  {
20795   
20796     template : {
20797         tag: 'div',
20798         cls: 'datepicker dropdown-menu roo-dynamic',
20799         cn: [
20800             {
20801                 tag: 'div',
20802                 cls: 'datepicker-months',
20803                 cn: [
20804                 {
20805                     tag: 'table',
20806                     cls: 'table-condensed',
20807                     cn:[
20808                         Roo.bootstrap.DateField.content
20809                     ]
20810                 }
20811                 ]
20812             }
20813         ]
20814     }
20815 });
20816
20817  
20818
20819  
20820  /*
20821  * - LGPL
20822  *
20823  * CheckBox
20824  * 
20825  */
20826
20827 /**
20828  * @class Roo.bootstrap.CheckBox
20829  * @extends Roo.bootstrap.Input
20830  * Bootstrap CheckBox class
20831  * 
20832  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20833  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20834  * @cfg {String} boxLabel The text that appears beside the checkbox
20835  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20836  * @cfg {Boolean} checked initnal the element
20837  * @cfg {Boolean} inline inline the element (default false)
20838  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20839  * @cfg {String} tooltip label tooltip
20840  * 
20841  * @constructor
20842  * Create a new CheckBox
20843  * @param {Object} config The config object
20844  */
20845
20846 Roo.bootstrap.CheckBox = function(config){
20847     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20848    
20849     this.addEvents({
20850         /**
20851         * @event check
20852         * Fires when the element is checked or unchecked.
20853         * @param {Roo.bootstrap.CheckBox} this This input
20854         * @param {Boolean} checked The new checked value
20855         */
20856        check : true,
20857        /**
20858         * @event click
20859         * Fires when the element is click.
20860         * @param {Roo.bootstrap.CheckBox} this This input
20861         */
20862        click : true
20863     });
20864     
20865 };
20866
20867 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20868   
20869     inputType: 'checkbox',
20870     inputValue: 1,
20871     valueOff: 0,
20872     boxLabel: false,
20873     checked: false,
20874     weight : false,
20875     inline: false,
20876     tooltip : '',
20877     
20878     getAutoCreate : function()
20879     {
20880         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20881         
20882         var id = Roo.id();
20883         
20884         var cfg = {};
20885         
20886         cfg.cls = 'form-group ' + this.inputType; //input-group
20887         
20888         if(this.inline){
20889             cfg.cls += ' ' + this.inputType + '-inline';
20890         }
20891         
20892         var input =  {
20893             tag: 'input',
20894             id : id,
20895             type : this.inputType,
20896             value : this.inputValue,
20897             cls : 'roo-' + this.inputType, //'form-box',
20898             placeholder : this.placeholder || ''
20899             
20900         };
20901         
20902         if(this.inputType != 'radio'){
20903             var hidden =  {
20904                 tag: 'input',
20905                 type : 'hidden',
20906                 cls : 'roo-hidden-value',
20907                 value : this.checked ? this.inputValue : this.valueOff
20908             };
20909         }
20910         
20911             
20912         if (this.weight) { // Validity check?
20913             cfg.cls += " " + this.inputType + "-" + this.weight;
20914         }
20915         
20916         if (this.disabled) {
20917             input.disabled=true;
20918         }
20919         
20920         if(this.checked){
20921             input.checked = this.checked;
20922         }
20923         
20924         if (this.name) {
20925             
20926             input.name = this.name;
20927             
20928             if(this.inputType != 'radio'){
20929                 hidden.name = this.name;
20930                 input.name = '_hidden_' + this.name;
20931             }
20932         }
20933         
20934         if (this.size) {
20935             input.cls += ' input-' + this.size;
20936         }
20937         
20938         var settings=this;
20939         
20940         ['xs','sm','md','lg'].map(function(size){
20941             if (settings[size]) {
20942                 cfg.cls += ' col-' + size + '-' + settings[size];
20943             }
20944         });
20945         
20946         var inputblock = input;
20947          
20948         if (this.before || this.after) {
20949             
20950             inputblock = {
20951                 cls : 'input-group',
20952                 cn :  [] 
20953             };
20954             
20955             if (this.before) {
20956                 inputblock.cn.push({
20957                     tag :'span',
20958                     cls : 'input-group-addon',
20959                     html : this.before
20960                 });
20961             }
20962             
20963             inputblock.cn.push(input);
20964             
20965             if(this.inputType != 'radio'){
20966                 inputblock.cn.push(hidden);
20967             }
20968             
20969             if (this.after) {
20970                 inputblock.cn.push({
20971                     tag :'span',
20972                     cls : 'input-group-addon',
20973                     html : this.after
20974                 });
20975             }
20976             
20977         }
20978         
20979         if (align ==='left' && this.fieldLabel.length) {
20980 //                Roo.log("left and has label");
20981             cfg.cn = [
20982                 {
20983                     tag: 'label',
20984                     'for' :  id,
20985                     cls : 'control-label',
20986                     html : this.fieldLabel
20987                 },
20988                 {
20989                     cls : "", 
20990                     cn: [
20991                         inputblock
20992                     ]
20993                 }
20994             ];
20995             
20996             if(this.labelWidth > 12){
20997                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20998             }
20999             
21000             if(this.labelWidth < 13 && this.labelmd == 0){
21001                 this.labelmd = this.labelWidth;
21002             }
21003             
21004             if(this.labellg > 0){
21005                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21006                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21007             }
21008             
21009             if(this.labelmd > 0){
21010                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21011                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21012             }
21013             
21014             if(this.labelsm > 0){
21015                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21016                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21017             }
21018             
21019             if(this.labelxs > 0){
21020                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21021                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21022             }
21023             
21024         } else if ( this.fieldLabel.length) {
21025 //                Roo.log(" label");
21026                 cfg.cn = [
21027                    
21028                     {
21029                         tag: this.boxLabel ? 'span' : 'label',
21030                         'for': id,
21031                         cls: 'control-label box-input-label',
21032                         //cls : 'input-group-addon',
21033                         html : this.fieldLabel
21034                     },
21035                     
21036                     inputblock
21037                     
21038                 ];
21039
21040         } else {
21041             
21042 //                Roo.log(" no label && no align");
21043                 cfg.cn = [  inputblock ] ;
21044                 
21045                 
21046         }
21047         
21048         if(this.boxLabel){
21049              var boxLabelCfg = {
21050                 tag: 'label',
21051                 //'for': id, // box label is handled by onclick - so no for...
21052                 cls: 'box-label',
21053                 html: this.boxLabel
21054             };
21055             
21056             if(this.tooltip){
21057                 boxLabelCfg.tooltip = this.tooltip;
21058             }
21059              
21060             cfg.cn.push(boxLabelCfg);
21061         }
21062         
21063         if(this.inputType != 'radio'){
21064             cfg.cn.push(hidden);
21065         }
21066         
21067         return cfg;
21068         
21069     },
21070     
21071     /**
21072      * return the real input element.
21073      */
21074     inputEl: function ()
21075     {
21076         return this.el.select('input.roo-' + this.inputType,true).first();
21077     },
21078     hiddenEl: function ()
21079     {
21080         return this.el.select('input.roo-hidden-value',true).first();
21081     },
21082     
21083     labelEl: function()
21084     {
21085         return this.el.select('label.control-label',true).first();
21086     },
21087     /* depricated... */
21088     
21089     label: function()
21090     {
21091         return this.labelEl();
21092     },
21093     
21094     boxLabelEl: function()
21095     {
21096         return this.el.select('label.box-label',true).first();
21097     },
21098     
21099     initEvents : function()
21100     {
21101 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21102         
21103         this.inputEl().on('click', this.onClick,  this);
21104         
21105         if (this.boxLabel) { 
21106             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21107         }
21108         
21109         this.startValue = this.getValue();
21110         
21111         if(this.groupId){
21112             Roo.bootstrap.CheckBox.register(this);
21113         }
21114     },
21115     
21116     onClick : function(e)
21117     {   
21118         if(this.fireEvent('click', this, e) !== false){
21119             this.setChecked(!this.checked);
21120         }
21121         
21122     },
21123     
21124     setChecked : function(state,suppressEvent)
21125     {
21126         this.startValue = this.getValue();
21127
21128         if(this.inputType == 'radio'){
21129             
21130             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21131                 e.dom.checked = false;
21132             });
21133             
21134             this.inputEl().dom.checked = true;
21135             
21136             this.inputEl().dom.value = this.inputValue;
21137             
21138             if(suppressEvent !== true){
21139                 this.fireEvent('check', this, true);
21140             }
21141             
21142             this.validate();
21143             
21144             return;
21145         }
21146         
21147         this.checked = state;
21148         
21149         this.inputEl().dom.checked = state;
21150         
21151         
21152         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21153         
21154         if(suppressEvent !== true){
21155             this.fireEvent('check', this, state);
21156         }
21157         
21158         this.validate();
21159     },
21160     
21161     getValue : function()
21162     {
21163         if(this.inputType == 'radio'){
21164             return this.getGroupValue();
21165         }
21166         
21167         return this.hiddenEl().dom.value;
21168         
21169     },
21170     
21171     getGroupValue : function()
21172     {
21173         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21174             return '';
21175         }
21176         
21177         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21178     },
21179     
21180     setValue : function(v,suppressEvent)
21181     {
21182         if(this.inputType == 'radio'){
21183             this.setGroupValue(v, suppressEvent);
21184             return;
21185         }
21186         
21187         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21188         
21189         this.validate();
21190     },
21191     
21192     setGroupValue : function(v, suppressEvent)
21193     {
21194         this.startValue = this.getValue();
21195         
21196         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21197             e.dom.checked = false;
21198             
21199             if(e.dom.value == v){
21200                 e.dom.checked = true;
21201             }
21202         });
21203         
21204         if(suppressEvent !== true){
21205             this.fireEvent('check', this, true);
21206         }
21207
21208         this.validate();
21209         
21210         return;
21211     },
21212     
21213     validate : function()
21214     {
21215         if(this.getVisibilityEl().hasClass('hidden')){
21216             return true;
21217         }
21218         
21219         if(
21220                 this.disabled || 
21221                 (this.inputType == 'radio' && this.validateRadio()) ||
21222                 (this.inputType == 'checkbox' && this.validateCheckbox())
21223         ){
21224             this.markValid();
21225             return true;
21226         }
21227         
21228         this.markInvalid();
21229         return false;
21230     },
21231     
21232     validateRadio : function()
21233     {
21234         if(this.getVisibilityEl().hasClass('hidden')){
21235             return true;
21236         }
21237         
21238         if(this.allowBlank){
21239             return true;
21240         }
21241         
21242         var valid = false;
21243         
21244         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21245             if(!e.dom.checked){
21246                 return;
21247             }
21248             
21249             valid = true;
21250             
21251             return false;
21252         });
21253         
21254         return valid;
21255     },
21256     
21257     validateCheckbox : function()
21258     {
21259         if(!this.groupId){
21260             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21261             //return (this.getValue() == this.inputValue) ? true : false;
21262         }
21263         
21264         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21265         
21266         if(!group){
21267             return false;
21268         }
21269         
21270         var r = false;
21271         
21272         for(var i in group){
21273             if(group[i].el.isVisible(true)){
21274                 r = false;
21275                 break;
21276             }
21277             
21278             r = true;
21279         }
21280         
21281         for(var i in group){
21282             if(r){
21283                 break;
21284             }
21285             
21286             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21287         }
21288         
21289         return r;
21290     },
21291     
21292     /**
21293      * Mark this field as valid
21294      */
21295     markValid : function()
21296     {
21297         var _this = this;
21298         
21299         this.fireEvent('valid', this);
21300         
21301         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21302         
21303         if(this.groupId){
21304             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21305         }
21306         
21307         if(label){
21308             label.markValid();
21309         }
21310
21311         if(this.inputType == 'radio'){
21312             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21313                 var fg = e.findParent('.form-group', false, true);
21314                 if (Roo.bootstrap.version == 3) {
21315                     fg.removeClass([_this.invalidClass, _this.validClass]);
21316                     fg.addClass(_this.validClass);
21317                 } else {
21318                     fg.removeClass(['is-valid', 'is-invalid']);
21319                     fg.addClass('is-valid');
21320                 }
21321             });
21322             
21323             return;
21324         }
21325
21326         if(!this.groupId){
21327             var fg = this.el.findParent('.form-group', false, true);
21328             if (Roo.bootstrap.version == 3) {
21329                 fg.removeClass([this.invalidClass, this.validClass]);
21330                 fg.addClass(this.validClass);
21331             } else {
21332                 fg.removeClass(['is-valid', 'is-invalid']);
21333                 fg.addClass('is-valid');
21334             }
21335             return;
21336         }
21337         
21338         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21339         
21340         if(!group){
21341             return;
21342         }
21343         
21344         for(var i in group){
21345             var fg = group[i].el.findParent('.form-group', false, true);
21346             if (Roo.bootstrap.version == 3) {
21347                 fg.removeClass([this.invalidClass, this.validClass]);
21348                 fg.addClass(this.validClass);
21349             } else {
21350                 fg.removeClass(['is-valid', 'is-invalid']);
21351                 fg.addClass('is-valid');
21352             }
21353         }
21354     },
21355     
21356      /**
21357      * Mark this field as invalid
21358      * @param {String} msg The validation message
21359      */
21360     markInvalid : function(msg)
21361     {
21362         if(this.allowBlank){
21363             return;
21364         }
21365         
21366         var _this = this;
21367         
21368         this.fireEvent('invalid', this, msg);
21369         
21370         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21371         
21372         if(this.groupId){
21373             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21374         }
21375         
21376         if(label){
21377             label.markInvalid();
21378         }
21379             
21380         if(this.inputType == 'radio'){
21381             
21382             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21383                 var fg = e.findParent('.form-group', false, true);
21384                 if (Roo.bootstrap.version == 3) {
21385                     fg.removeClass([_this.invalidClass, _this.validClass]);
21386                     fg.addClass(_this.invalidClass);
21387                 } else {
21388                     fg.removeClass(['is-invalid', 'is-valid']);
21389                     fg.addClass('is-invalid');
21390                 }
21391             });
21392             
21393             return;
21394         }
21395         
21396         if(!this.groupId){
21397             var fg = this.el.findParent('.form-group', false, true);
21398             if (Roo.bootstrap.version == 3) {
21399                 fg.removeClass([_this.invalidClass, _this.validClass]);
21400                 fg.addClass(_this.invalidClass);
21401             } else {
21402                 fg.removeClass(['is-invalid', 'is-valid']);
21403                 fg.addClass('is-invalid');
21404             }
21405             return;
21406         }
21407         
21408         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21409         
21410         if(!group){
21411             return;
21412         }
21413         
21414         for(var i in group){
21415             var fg = group[i].el.findParent('.form-group', false, true);
21416             if (Roo.bootstrap.version == 3) {
21417                 fg.removeClass([_this.invalidClass, _this.validClass]);
21418                 fg.addClass(_this.invalidClass);
21419             } else {
21420                 fg.removeClass(['is-invalid', 'is-valid']);
21421                 fg.addClass('is-invalid');
21422             }
21423         }
21424         
21425     },
21426     
21427     clearInvalid : function()
21428     {
21429         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21430         
21431         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21432         
21433         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21434         
21435         if (label && label.iconEl) {
21436             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21437             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21438         }
21439     },
21440     
21441     disable : function()
21442     {
21443         if(this.inputType != 'radio'){
21444             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21445             return;
21446         }
21447         
21448         var _this = this;
21449         
21450         if(this.rendered){
21451             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21452                 _this.getActionEl().addClass(this.disabledClass);
21453                 e.dom.disabled = true;
21454             });
21455         }
21456         
21457         this.disabled = true;
21458         this.fireEvent("disable", this);
21459         return this;
21460     },
21461
21462     enable : function()
21463     {
21464         if(this.inputType != 'radio'){
21465             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21466             return;
21467         }
21468         
21469         var _this = this;
21470         
21471         if(this.rendered){
21472             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21473                 _this.getActionEl().removeClass(this.disabledClass);
21474                 e.dom.disabled = false;
21475             });
21476         }
21477         
21478         this.disabled = false;
21479         this.fireEvent("enable", this);
21480         return this;
21481     },
21482     
21483     setBoxLabel : function(v)
21484     {
21485         this.boxLabel = v;
21486         
21487         if(this.rendered){
21488             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21489         }
21490     }
21491
21492 });
21493
21494 Roo.apply(Roo.bootstrap.CheckBox, {
21495     
21496     groups: {},
21497     
21498      /**
21499     * register a CheckBox Group
21500     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21501     */
21502     register : function(checkbox)
21503     {
21504         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21505             this.groups[checkbox.groupId] = {};
21506         }
21507         
21508         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21509             return;
21510         }
21511         
21512         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21513         
21514     },
21515     /**
21516     * fetch a CheckBox Group based on the group ID
21517     * @param {string} the group ID
21518     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21519     */
21520     get: function(groupId) {
21521         if (typeof(this.groups[groupId]) == 'undefined') {
21522             return false;
21523         }
21524         
21525         return this.groups[groupId] ;
21526     }
21527     
21528     
21529 });
21530 /*
21531  * - LGPL
21532  *
21533  * RadioItem
21534  * 
21535  */
21536
21537 /**
21538  * @class Roo.bootstrap.Radio
21539  * @extends Roo.bootstrap.Component
21540  * Bootstrap Radio class
21541  * @cfg {String} boxLabel - the label associated
21542  * @cfg {String} value - the value of radio
21543  * 
21544  * @constructor
21545  * Create a new Radio
21546  * @param {Object} config The config object
21547  */
21548 Roo.bootstrap.Radio = function(config){
21549     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21550     
21551 };
21552
21553 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21554     
21555     boxLabel : '',
21556     
21557     value : '',
21558     
21559     getAutoCreate : function()
21560     {
21561         var cfg = {
21562             tag : 'div',
21563             cls : 'form-group radio',
21564             cn : [
21565                 {
21566                     tag : 'label',
21567                     cls : 'box-label',
21568                     html : this.boxLabel
21569                 }
21570             ]
21571         };
21572         
21573         return cfg;
21574     },
21575     
21576     initEvents : function() 
21577     {
21578         this.parent().register(this);
21579         
21580         this.el.on('click', this.onClick, this);
21581         
21582     },
21583     
21584     onClick : function(e)
21585     {
21586         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21587             this.setChecked(true);
21588         }
21589     },
21590     
21591     setChecked : function(state, suppressEvent)
21592     {
21593         this.parent().setValue(this.value, suppressEvent);
21594         
21595     },
21596     
21597     setBoxLabel : function(v)
21598     {
21599         this.boxLabel = v;
21600         
21601         if(this.rendered){
21602             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21603         }
21604     }
21605     
21606 });
21607  
21608
21609  /*
21610  * - LGPL
21611  *
21612  * Input
21613  * 
21614  */
21615
21616 /**
21617  * @class Roo.bootstrap.SecurePass
21618  * @extends Roo.bootstrap.Input
21619  * Bootstrap SecurePass class
21620  *
21621  * 
21622  * @constructor
21623  * Create a new SecurePass
21624  * @param {Object} config The config object
21625  */
21626  
21627 Roo.bootstrap.SecurePass = function (config) {
21628     // these go here, so the translation tool can replace them..
21629     this.errors = {
21630         PwdEmpty: "Please type a password, and then retype it to confirm.",
21631         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21632         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21633         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21634         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21635         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21636         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21637         TooWeak: "Your password is Too Weak."
21638     },
21639     this.meterLabel = "Password strength:";
21640     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21641     this.meterClass = [
21642         "roo-password-meter-tooweak", 
21643         "roo-password-meter-weak", 
21644         "roo-password-meter-medium", 
21645         "roo-password-meter-strong", 
21646         "roo-password-meter-grey"
21647     ];
21648     
21649     this.errors = {};
21650     
21651     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21652 }
21653
21654 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21655     /**
21656      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21657      * {
21658      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21659      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21660      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21661      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21662      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21663      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21664      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21665      * })
21666      */
21667     // private
21668     
21669     meterWidth: 300,
21670     errorMsg :'',    
21671     errors: false,
21672     imageRoot: '/',
21673     /**
21674      * @cfg {String/Object} Label for the strength meter (defaults to
21675      * 'Password strength:')
21676      */
21677     // private
21678     meterLabel: '',
21679     /**
21680      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21681      * ['Weak', 'Medium', 'Strong'])
21682      */
21683     // private    
21684     pwdStrengths: false,    
21685     // private
21686     strength: 0,
21687     // private
21688     _lastPwd: null,
21689     // private
21690     kCapitalLetter: 0,
21691     kSmallLetter: 1,
21692     kDigit: 2,
21693     kPunctuation: 3,
21694     
21695     insecure: false,
21696     // private
21697     initEvents: function ()
21698     {
21699         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21700
21701         if (this.el.is('input[type=password]') && Roo.isSafari) {
21702             this.el.on('keydown', this.SafariOnKeyDown, this);
21703         }
21704
21705         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21706     },
21707     // private
21708     onRender: function (ct, position)
21709     {
21710         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21711         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21712         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21713
21714         this.trigger.createChild({
21715                    cn: [
21716                     {
21717                     //id: 'PwdMeter',
21718                     tag: 'div',
21719                     cls: 'roo-password-meter-grey col-xs-12',
21720                     style: {
21721                         //width: 0,
21722                         //width: this.meterWidth + 'px'                                                
21723                         }
21724                     },
21725                     {                            
21726                          cls: 'roo-password-meter-text'                          
21727                     }
21728                 ]            
21729         });
21730
21731          
21732         if (this.hideTrigger) {
21733             this.trigger.setDisplayed(false);
21734         }
21735         this.setSize(this.width || '', this.height || '');
21736     },
21737     // private
21738     onDestroy: function ()
21739     {
21740         if (this.trigger) {
21741             this.trigger.removeAllListeners();
21742             this.trigger.remove();
21743         }
21744         if (this.wrap) {
21745             this.wrap.remove();
21746         }
21747         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21748     },
21749     // private
21750     checkStrength: function ()
21751     {
21752         var pwd = this.inputEl().getValue();
21753         if (pwd == this._lastPwd) {
21754             return;
21755         }
21756
21757         var strength;
21758         if (this.ClientSideStrongPassword(pwd)) {
21759             strength = 3;
21760         } else if (this.ClientSideMediumPassword(pwd)) {
21761             strength = 2;
21762         } else if (this.ClientSideWeakPassword(pwd)) {
21763             strength = 1;
21764         } else {
21765             strength = 0;
21766         }
21767         
21768         Roo.log('strength1: ' + strength);
21769         
21770         //var pm = this.trigger.child('div/div/div').dom;
21771         var pm = this.trigger.child('div/div');
21772         pm.removeClass(this.meterClass);
21773         pm.addClass(this.meterClass[strength]);
21774                 
21775         
21776         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21777                 
21778         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21779         
21780         this._lastPwd = pwd;
21781     },
21782     reset: function ()
21783     {
21784         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21785         
21786         this._lastPwd = '';
21787         
21788         var pm = this.trigger.child('div/div');
21789         pm.removeClass(this.meterClass);
21790         pm.addClass('roo-password-meter-grey');        
21791         
21792         
21793         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21794         
21795         pt.innerHTML = '';
21796         this.inputEl().dom.type='password';
21797     },
21798     // private
21799     validateValue: function (value)
21800     {
21801         
21802         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21803             return false;
21804         }
21805         if (value.length == 0) {
21806             if (this.allowBlank) {
21807                 this.clearInvalid();
21808                 return true;
21809             }
21810
21811             this.markInvalid(this.errors.PwdEmpty);
21812             this.errorMsg = this.errors.PwdEmpty;
21813             return false;
21814         }
21815         
21816         if(this.insecure){
21817             return true;
21818         }
21819         
21820         if ('[\x21-\x7e]*'.match(value)) {
21821             this.markInvalid(this.errors.PwdBadChar);
21822             this.errorMsg = this.errors.PwdBadChar;
21823             return false;
21824         }
21825         if (value.length < 6) {
21826             this.markInvalid(this.errors.PwdShort);
21827             this.errorMsg = this.errors.PwdShort;
21828             return false;
21829         }
21830         if (value.length > 16) {
21831             this.markInvalid(this.errors.PwdLong);
21832             this.errorMsg = this.errors.PwdLong;
21833             return false;
21834         }
21835         var strength;
21836         if (this.ClientSideStrongPassword(value)) {
21837             strength = 3;
21838         } else if (this.ClientSideMediumPassword(value)) {
21839             strength = 2;
21840         } else if (this.ClientSideWeakPassword(value)) {
21841             strength = 1;
21842         } else {
21843             strength = 0;
21844         }
21845
21846         
21847         if (strength < 2) {
21848             //this.markInvalid(this.errors.TooWeak);
21849             this.errorMsg = this.errors.TooWeak;
21850             //return false;
21851         }
21852         
21853         
21854         console.log('strength2: ' + strength);
21855         
21856         //var pm = this.trigger.child('div/div/div').dom;
21857         
21858         var pm = this.trigger.child('div/div');
21859         pm.removeClass(this.meterClass);
21860         pm.addClass(this.meterClass[strength]);
21861                 
21862         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21863                 
21864         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21865         
21866         this.errorMsg = ''; 
21867         return true;
21868     },
21869     // private
21870     CharacterSetChecks: function (type)
21871     {
21872         this.type = type;
21873         this.fResult = false;
21874     },
21875     // private
21876     isctype: function (character, type)
21877     {
21878         switch (type) {  
21879             case this.kCapitalLetter:
21880                 if (character >= 'A' && character <= 'Z') {
21881                     return true;
21882                 }
21883                 break;
21884             
21885             case this.kSmallLetter:
21886                 if (character >= 'a' && character <= 'z') {
21887                     return true;
21888                 }
21889                 break;
21890             
21891             case this.kDigit:
21892                 if (character >= '0' && character <= '9') {
21893                     return true;
21894                 }
21895                 break;
21896             
21897             case this.kPunctuation:
21898                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21899                     return true;
21900                 }
21901                 break;
21902             
21903             default:
21904                 return false;
21905         }
21906
21907     },
21908     // private
21909     IsLongEnough: function (pwd, size)
21910     {
21911         return !(pwd == null || isNaN(size) || pwd.length < size);
21912     },
21913     // private
21914     SpansEnoughCharacterSets: function (word, nb)
21915     {
21916         if (!this.IsLongEnough(word, nb))
21917         {
21918             return false;
21919         }
21920
21921         var characterSetChecks = new Array(
21922             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21923             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21924         );
21925         
21926         for (var index = 0; index < word.length; ++index) {
21927             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21928                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21929                     characterSetChecks[nCharSet].fResult = true;
21930                     break;
21931                 }
21932             }
21933         }
21934
21935         var nCharSets = 0;
21936         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21937             if (characterSetChecks[nCharSet].fResult) {
21938                 ++nCharSets;
21939             }
21940         }
21941
21942         if (nCharSets < nb) {
21943             return false;
21944         }
21945         return true;
21946     },
21947     // private
21948     ClientSideStrongPassword: function (pwd)
21949     {
21950         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21951     },
21952     // private
21953     ClientSideMediumPassword: function (pwd)
21954     {
21955         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21956     },
21957     // private
21958     ClientSideWeakPassword: function (pwd)
21959     {
21960         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21961     }
21962           
21963 })//<script type="text/javascript">
21964
21965 /*
21966  * Based  Ext JS Library 1.1.1
21967  * Copyright(c) 2006-2007, Ext JS, LLC.
21968  * LGPL
21969  *
21970  */
21971  
21972 /**
21973  * @class Roo.HtmlEditorCore
21974  * @extends Roo.Component
21975  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21976  *
21977  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21978  */
21979
21980 Roo.HtmlEditorCore = function(config){
21981     
21982     
21983     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21984     
21985     
21986     this.addEvents({
21987         /**
21988          * @event initialize
21989          * Fires when the editor is fully initialized (including the iframe)
21990          * @param {Roo.HtmlEditorCore} this
21991          */
21992         initialize: true,
21993         /**
21994          * @event activate
21995          * Fires when the editor is first receives the focus. Any insertion must wait
21996          * until after this event.
21997          * @param {Roo.HtmlEditorCore} this
21998          */
21999         activate: true,
22000          /**
22001          * @event beforesync
22002          * Fires before the textarea is updated with content from the editor iframe. Return false
22003          * to cancel the sync.
22004          * @param {Roo.HtmlEditorCore} this
22005          * @param {String} html
22006          */
22007         beforesync: true,
22008          /**
22009          * @event beforepush
22010          * Fires before the iframe editor is updated with content from the textarea. Return false
22011          * to cancel the push.
22012          * @param {Roo.HtmlEditorCore} this
22013          * @param {String} html
22014          */
22015         beforepush: true,
22016          /**
22017          * @event sync
22018          * Fires when the textarea is updated with content from the editor iframe.
22019          * @param {Roo.HtmlEditorCore} this
22020          * @param {String} html
22021          */
22022         sync: true,
22023          /**
22024          * @event push
22025          * Fires when the iframe editor is updated with content from the textarea.
22026          * @param {Roo.HtmlEditorCore} this
22027          * @param {String} html
22028          */
22029         push: true,
22030         
22031         /**
22032          * @event editorevent
22033          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22034          * @param {Roo.HtmlEditorCore} this
22035          */
22036         editorevent: true
22037         
22038     });
22039     
22040     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22041     
22042     // defaults : white / black...
22043     this.applyBlacklists();
22044     
22045     
22046     
22047 };
22048
22049
22050 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22051
22052
22053      /**
22054      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22055      */
22056     
22057     owner : false,
22058     
22059      /**
22060      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22061      *                        Roo.resizable.
22062      */
22063     resizable : false,
22064      /**
22065      * @cfg {Number} height (in pixels)
22066      */   
22067     height: 300,
22068    /**
22069      * @cfg {Number} width (in pixels)
22070      */   
22071     width: 500,
22072     
22073     /**
22074      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22075      * 
22076      */
22077     stylesheets: false,
22078     
22079     // id of frame..
22080     frameId: false,
22081     
22082     // private properties
22083     validationEvent : false,
22084     deferHeight: true,
22085     initialized : false,
22086     activated : false,
22087     sourceEditMode : false,
22088     onFocus : Roo.emptyFn,
22089     iframePad:3,
22090     hideMode:'offsets',
22091     
22092     clearUp: true,
22093     
22094     // blacklist + whitelisted elements..
22095     black: false,
22096     white: false,
22097      
22098     bodyCls : '',
22099
22100     /**
22101      * Protected method that will not generally be called directly. It
22102      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22103      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22104      */
22105     getDocMarkup : function(){
22106         // body styles..
22107         var st = '';
22108         
22109         // inherit styels from page...?? 
22110         if (this.stylesheets === false) {
22111             
22112             Roo.get(document.head).select('style').each(function(node) {
22113                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22114             });
22115             
22116             Roo.get(document.head).select('link').each(function(node) { 
22117                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22118             });
22119             
22120         } else if (!this.stylesheets.length) {
22121                 // simple..
22122                 st = '<style type="text/css">' +
22123                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22124                    '</style>';
22125         } else { 
22126             st = '<style type="text/css">' +
22127                     this.stylesheets +
22128                 '</style>';
22129         }
22130         
22131         st +=  '<style type="text/css">' +
22132             'IMG { cursor: pointer } ' +
22133         '</style>';
22134
22135         var cls = 'roo-htmleditor-body';
22136         
22137         if(this.bodyCls.length){
22138             cls += ' ' + this.bodyCls;
22139         }
22140         
22141         return '<html><head>' + st  +
22142             //<style type="text/css">' +
22143             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22144             //'</style>' +
22145             ' </head><body class="' +  cls + '"></body></html>';
22146     },
22147
22148     // private
22149     onRender : function(ct, position)
22150     {
22151         var _t = this;
22152         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22153         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22154         
22155         
22156         this.el.dom.style.border = '0 none';
22157         this.el.dom.setAttribute('tabIndex', -1);
22158         this.el.addClass('x-hidden hide');
22159         
22160         
22161         
22162         if(Roo.isIE){ // fix IE 1px bogus margin
22163             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22164         }
22165        
22166         
22167         this.frameId = Roo.id();
22168         
22169          
22170         
22171         var iframe = this.owner.wrap.createChild({
22172             tag: 'iframe',
22173             cls: 'form-control', // bootstrap..
22174             id: this.frameId,
22175             name: this.frameId,
22176             frameBorder : 'no',
22177             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22178         }, this.el
22179         );
22180         
22181         
22182         this.iframe = iframe.dom;
22183
22184          this.assignDocWin();
22185         
22186         this.doc.designMode = 'on';
22187        
22188         this.doc.open();
22189         this.doc.write(this.getDocMarkup());
22190         this.doc.close();
22191
22192         
22193         var task = { // must defer to wait for browser to be ready
22194             run : function(){
22195                 //console.log("run task?" + this.doc.readyState);
22196                 this.assignDocWin();
22197                 if(this.doc.body || this.doc.readyState == 'complete'){
22198                     try {
22199                         this.doc.designMode="on";
22200                     } catch (e) {
22201                         return;
22202                     }
22203                     Roo.TaskMgr.stop(task);
22204                     this.initEditor.defer(10, this);
22205                 }
22206             },
22207             interval : 10,
22208             duration: 10000,
22209             scope: this
22210         };
22211         Roo.TaskMgr.start(task);
22212
22213     },
22214
22215     // private
22216     onResize : function(w, h)
22217     {
22218          Roo.log('resize: ' +w + ',' + h );
22219         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22220         if(!this.iframe){
22221             return;
22222         }
22223         if(typeof w == 'number'){
22224             
22225             this.iframe.style.width = w + 'px';
22226         }
22227         if(typeof h == 'number'){
22228             
22229             this.iframe.style.height = h + 'px';
22230             if(this.doc){
22231                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22232             }
22233         }
22234         
22235     },
22236
22237     /**
22238      * Toggles the editor between standard and source edit mode.
22239      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22240      */
22241     toggleSourceEdit : function(sourceEditMode){
22242         
22243         this.sourceEditMode = sourceEditMode === true;
22244         
22245         if(this.sourceEditMode){
22246  
22247             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22248             
22249         }else{
22250             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22251             //this.iframe.className = '';
22252             this.deferFocus();
22253         }
22254         //this.setSize(this.owner.wrap.getSize());
22255         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22256     },
22257
22258     
22259   
22260
22261     /**
22262      * Protected method that will not generally be called directly. If you need/want
22263      * custom HTML cleanup, this is the method you should override.
22264      * @param {String} html The HTML to be cleaned
22265      * return {String} The cleaned HTML
22266      */
22267     cleanHtml : function(html){
22268         html = String(html);
22269         if(html.length > 5){
22270             if(Roo.isSafari){ // strip safari nonsense
22271                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22272             }
22273         }
22274         if(html == '&nbsp;'){
22275             html = '';
22276         }
22277         return html;
22278     },
22279
22280     /**
22281      * HTML Editor -> Textarea
22282      * Protected method that will not generally be called directly. Syncs the contents
22283      * of the editor iframe with the textarea.
22284      */
22285     syncValue : function(){
22286         if(this.initialized){
22287             var bd = (this.doc.body || this.doc.documentElement);
22288             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22289             var html = bd.innerHTML;
22290             if(Roo.isSafari){
22291                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22292                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22293                 if(m && m[1]){
22294                     html = '<div style="'+m[0]+'">' + html + '</div>';
22295                 }
22296             }
22297             html = this.cleanHtml(html);
22298             // fix up the special chars.. normaly like back quotes in word...
22299             // however we do not want to do this with chinese..
22300             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22301                 var cc = b.charCodeAt();
22302                 if (
22303                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22304                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22305                     (cc >= 0xf900 && cc < 0xfb00 )
22306                 ) {
22307                         return b;
22308                 }
22309                 return "&#"+cc+";" 
22310             });
22311             if(this.owner.fireEvent('beforesync', this, html) !== false){
22312                 this.el.dom.value = html;
22313                 this.owner.fireEvent('sync', this, html);
22314             }
22315         }
22316     },
22317
22318     /**
22319      * Protected method that will not generally be called directly. Pushes the value of the textarea
22320      * into the iframe editor.
22321      */
22322     pushValue : function(){
22323         if(this.initialized){
22324             var v = this.el.dom.value.trim();
22325             
22326 //            if(v.length < 1){
22327 //                v = '&#160;';
22328 //            }
22329             
22330             if(this.owner.fireEvent('beforepush', this, v) !== false){
22331                 var d = (this.doc.body || this.doc.documentElement);
22332                 d.innerHTML = v;
22333                 this.cleanUpPaste();
22334                 this.el.dom.value = d.innerHTML;
22335                 this.owner.fireEvent('push', this, v);
22336             }
22337         }
22338     },
22339
22340     // private
22341     deferFocus : function(){
22342         this.focus.defer(10, this);
22343     },
22344
22345     // doc'ed in Field
22346     focus : function(){
22347         if(this.win && !this.sourceEditMode){
22348             this.win.focus();
22349         }else{
22350             this.el.focus();
22351         }
22352     },
22353     
22354     assignDocWin: function()
22355     {
22356         var iframe = this.iframe;
22357         
22358          if(Roo.isIE){
22359             this.doc = iframe.contentWindow.document;
22360             this.win = iframe.contentWindow;
22361         } else {
22362 //            if (!Roo.get(this.frameId)) {
22363 //                return;
22364 //            }
22365 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22366 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22367             
22368             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22369                 return;
22370             }
22371             
22372             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22373             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22374         }
22375     },
22376     
22377     // private
22378     initEditor : function(){
22379         //console.log("INIT EDITOR");
22380         this.assignDocWin();
22381         
22382         
22383         
22384         this.doc.designMode="on";
22385         this.doc.open();
22386         this.doc.write(this.getDocMarkup());
22387         this.doc.close();
22388         
22389         var dbody = (this.doc.body || this.doc.documentElement);
22390         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22391         // this copies styles from the containing element into thsi one..
22392         // not sure why we need all of this..
22393         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22394         
22395         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22396         //ss['background-attachment'] = 'fixed'; // w3c
22397         dbody.bgProperties = 'fixed'; // ie
22398         //Roo.DomHelper.applyStyles(dbody, ss);
22399         Roo.EventManager.on(this.doc, {
22400             //'mousedown': this.onEditorEvent,
22401             'mouseup': this.onEditorEvent,
22402             'dblclick': this.onEditorEvent,
22403             'click': this.onEditorEvent,
22404             'keyup': this.onEditorEvent,
22405             buffer:100,
22406             scope: this
22407         });
22408         if(Roo.isGecko){
22409             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22410         }
22411         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22412             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22413         }
22414         this.initialized = true;
22415
22416         this.owner.fireEvent('initialize', this);
22417         this.pushValue();
22418     },
22419
22420     // private
22421     onDestroy : function(){
22422         
22423         
22424         
22425         if(this.rendered){
22426             
22427             //for (var i =0; i < this.toolbars.length;i++) {
22428             //    // fixme - ask toolbars for heights?
22429             //    this.toolbars[i].onDestroy();
22430            // }
22431             
22432             //this.wrap.dom.innerHTML = '';
22433             //this.wrap.remove();
22434         }
22435     },
22436
22437     // private
22438     onFirstFocus : function(){
22439         
22440         this.assignDocWin();
22441         
22442         
22443         this.activated = true;
22444          
22445     
22446         if(Roo.isGecko){ // prevent silly gecko errors
22447             this.win.focus();
22448             var s = this.win.getSelection();
22449             if(!s.focusNode || s.focusNode.nodeType != 3){
22450                 var r = s.getRangeAt(0);
22451                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22452                 r.collapse(true);
22453                 this.deferFocus();
22454             }
22455             try{
22456                 this.execCmd('useCSS', true);
22457                 this.execCmd('styleWithCSS', false);
22458             }catch(e){}
22459         }
22460         this.owner.fireEvent('activate', this);
22461     },
22462
22463     // private
22464     adjustFont: function(btn){
22465         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22466         //if(Roo.isSafari){ // safari
22467         //    adjust *= 2;
22468        // }
22469         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22470         if(Roo.isSafari){ // safari
22471             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22472             v =  (v < 10) ? 10 : v;
22473             v =  (v > 48) ? 48 : v;
22474             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22475             
22476         }
22477         
22478         
22479         v = Math.max(1, v+adjust);
22480         
22481         this.execCmd('FontSize', v  );
22482     },
22483
22484     onEditorEvent : function(e)
22485     {
22486         this.owner.fireEvent('editorevent', this, e);
22487       //  this.updateToolbar();
22488         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22489     },
22490
22491     insertTag : function(tg)
22492     {
22493         // could be a bit smarter... -> wrap the current selected tRoo..
22494         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22495             
22496             range = this.createRange(this.getSelection());
22497             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22498             wrappingNode.appendChild(range.extractContents());
22499             range.insertNode(wrappingNode);
22500
22501             return;
22502             
22503             
22504             
22505         }
22506         this.execCmd("formatblock",   tg);
22507         
22508     },
22509     
22510     insertText : function(txt)
22511     {
22512         
22513         
22514         var range = this.createRange();
22515         range.deleteContents();
22516                //alert(Sender.getAttribute('label'));
22517                
22518         range.insertNode(this.doc.createTextNode(txt));
22519     } ,
22520     
22521      
22522
22523     /**
22524      * Executes a Midas editor command on the editor document and performs necessary focus and
22525      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22526      * @param {String} cmd The Midas command
22527      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22528      */
22529     relayCmd : function(cmd, value){
22530         this.win.focus();
22531         this.execCmd(cmd, value);
22532         this.owner.fireEvent('editorevent', this);
22533         //this.updateToolbar();
22534         this.owner.deferFocus();
22535     },
22536
22537     /**
22538      * Executes a Midas editor command directly on the editor document.
22539      * For visual commands, you should use {@link #relayCmd} instead.
22540      * <b>This should only be called after the editor is initialized.</b>
22541      * @param {String} cmd The Midas command
22542      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22543      */
22544     execCmd : function(cmd, value){
22545         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22546         this.syncValue();
22547     },
22548  
22549  
22550    
22551     /**
22552      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22553      * to insert tRoo.
22554      * @param {String} text | dom node.. 
22555      */
22556     insertAtCursor : function(text)
22557     {
22558         
22559         if(!this.activated){
22560             return;
22561         }
22562         /*
22563         if(Roo.isIE){
22564             this.win.focus();
22565             var r = this.doc.selection.createRange();
22566             if(r){
22567                 r.collapse(true);
22568                 r.pasteHTML(text);
22569                 this.syncValue();
22570                 this.deferFocus();
22571             
22572             }
22573             return;
22574         }
22575         */
22576         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22577             this.win.focus();
22578             
22579             
22580             // from jquery ui (MIT licenced)
22581             var range, node;
22582             var win = this.win;
22583             
22584             if (win.getSelection && win.getSelection().getRangeAt) {
22585                 range = win.getSelection().getRangeAt(0);
22586                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22587                 range.insertNode(node);
22588             } else if (win.document.selection && win.document.selection.createRange) {
22589                 // no firefox support
22590                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22591                 win.document.selection.createRange().pasteHTML(txt);
22592             } else {
22593                 // no firefox support
22594                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22595                 this.execCmd('InsertHTML', txt);
22596             } 
22597             
22598             this.syncValue();
22599             
22600             this.deferFocus();
22601         }
22602     },
22603  // private
22604     mozKeyPress : function(e){
22605         if(e.ctrlKey){
22606             var c = e.getCharCode(), cmd;
22607           
22608             if(c > 0){
22609                 c = String.fromCharCode(c).toLowerCase();
22610                 switch(c){
22611                     case 'b':
22612                         cmd = 'bold';
22613                         break;
22614                     case 'i':
22615                         cmd = 'italic';
22616                         break;
22617                     
22618                     case 'u':
22619                         cmd = 'underline';
22620                         break;
22621                     
22622                     case 'v':
22623                         this.cleanUpPaste.defer(100, this);
22624                         return;
22625                         
22626                 }
22627                 if(cmd){
22628                     this.win.focus();
22629                     this.execCmd(cmd);
22630                     this.deferFocus();
22631                     e.preventDefault();
22632                 }
22633                 
22634             }
22635         }
22636     },
22637
22638     // private
22639     fixKeys : function(){ // load time branching for fastest keydown performance
22640         if(Roo.isIE){
22641             return function(e){
22642                 var k = e.getKey(), r;
22643                 if(k == e.TAB){
22644                     e.stopEvent();
22645                     r = this.doc.selection.createRange();
22646                     if(r){
22647                         r.collapse(true);
22648                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22649                         this.deferFocus();
22650                     }
22651                     return;
22652                 }
22653                 
22654                 if(k == e.ENTER){
22655                     r = this.doc.selection.createRange();
22656                     if(r){
22657                         var target = r.parentElement();
22658                         if(!target || target.tagName.toLowerCase() != 'li'){
22659                             e.stopEvent();
22660                             r.pasteHTML('<br />');
22661                             r.collapse(false);
22662                             r.select();
22663                         }
22664                     }
22665                 }
22666                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22667                     this.cleanUpPaste.defer(100, this);
22668                     return;
22669                 }
22670                 
22671                 
22672             };
22673         }else if(Roo.isOpera){
22674             return function(e){
22675                 var k = e.getKey();
22676                 if(k == e.TAB){
22677                     e.stopEvent();
22678                     this.win.focus();
22679                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22680                     this.deferFocus();
22681                 }
22682                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22683                     this.cleanUpPaste.defer(100, this);
22684                     return;
22685                 }
22686                 
22687             };
22688         }else if(Roo.isSafari){
22689             return function(e){
22690                 var k = e.getKey();
22691                 
22692                 if(k == e.TAB){
22693                     e.stopEvent();
22694                     this.execCmd('InsertText','\t');
22695                     this.deferFocus();
22696                     return;
22697                 }
22698                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22699                     this.cleanUpPaste.defer(100, this);
22700                     return;
22701                 }
22702                 
22703              };
22704         }
22705     }(),
22706     
22707     getAllAncestors: function()
22708     {
22709         var p = this.getSelectedNode();
22710         var a = [];
22711         if (!p) {
22712             a.push(p); // push blank onto stack..
22713             p = this.getParentElement();
22714         }
22715         
22716         
22717         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22718             a.push(p);
22719             p = p.parentNode;
22720         }
22721         a.push(this.doc.body);
22722         return a;
22723     },
22724     lastSel : false,
22725     lastSelNode : false,
22726     
22727     
22728     getSelection : function() 
22729     {
22730         this.assignDocWin();
22731         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22732     },
22733     
22734     getSelectedNode: function() 
22735     {
22736         // this may only work on Gecko!!!
22737         
22738         // should we cache this!!!!
22739         
22740         
22741         
22742          
22743         var range = this.createRange(this.getSelection()).cloneRange();
22744         
22745         if (Roo.isIE) {
22746             var parent = range.parentElement();
22747             while (true) {
22748                 var testRange = range.duplicate();
22749                 testRange.moveToElementText(parent);
22750                 if (testRange.inRange(range)) {
22751                     break;
22752                 }
22753                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22754                     break;
22755                 }
22756                 parent = parent.parentElement;
22757             }
22758             return parent;
22759         }
22760         
22761         // is ancestor a text element.
22762         var ac =  range.commonAncestorContainer;
22763         if (ac.nodeType == 3) {
22764             ac = ac.parentNode;
22765         }
22766         
22767         var ar = ac.childNodes;
22768          
22769         var nodes = [];
22770         var other_nodes = [];
22771         var has_other_nodes = false;
22772         for (var i=0;i<ar.length;i++) {
22773             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22774                 continue;
22775             }
22776             // fullly contained node.
22777             
22778             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22779                 nodes.push(ar[i]);
22780                 continue;
22781             }
22782             
22783             // probably selected..
22784             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22785                 other_nodes.push(ar[i]);
22786                 continue;
22787             }
22788             // outer..
22789             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22790                 continue;
22791             }
22792             
22793             
22794             has_other_nodes = true;
22795         }
22796         if (!nodes.length && other_nodes.length) {
22797             nodes= other_nodes;
22798         }
22799         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22800             return false;
22801         }
22802         
22803         return nodes[0];
22804     },
22805     createRange: function(sel)
22806     {
22807         // this has strange effects when using with 
22808         // top toolbar - not sure if it's a great idea.
22809         //this.editor.contentWindow.focus();
22810         if (typeof sel != "undefined") {
22811             try {
22812                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22813             } catch(e) {
22814                 return this.doc.createRange();
22815             }
22816         } else {
22817             return this.doc.createRange();
22818         }
22819     },
22820     getParentElement: function()
22821     {
22822         
22823         this.assignDocWin();
22824         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22825         
22826         var range = this.createRange(sel);
22827          
22828         try {
22829             var p = range.commonAncestorContainer;
22830             while (p.nodeType == 3) { // text node
22831                 p = p.parentNode;
22832             }
22833             return p;
22834         } catch (e) {
22835             return null;
22836         }
22837     
22838     },
22839     /***
22840      *
22841      * Range intersection.. the hard stuff...
22842      *  '-1' = before
22843      *  '0' = hits..
22844      *  '1' = after.
22845      *         [ -- selected range --- ]
22846      *   [fail]                        [fail]
22847      *
22848      *    basically..
22849      *      if end is before start or  hits it. fail.
22850      *      if start is after end or hits it fail.
22851      *
22852      *   if either hits (but other is outside. - then it's not 
22853      *   
22854      *    
22855      **/
22856     
22857     
22858     // @see http://www.thismuchiknow.co.uk/?p=64.
22859     rangeIntersectsNode : function(range, node)
22860     {
22861         var nodeRange = node.ownerDocument.createRange();
22862         try {
22863             nodeRange.selectNode(node);
22864         } catch (e) {
22865             nodeRange.selectNodeContents(node);
22866         }
22867     
22868         var rangeStartRange = range.cloneRange();
22869         rangeStartRange.collapse(true);
22870     
22871         var rangeEndRange = range.cloneRange();
22872         rangeEndRange.collapse(false);
22873     
22874         var nodeStartRange = nodeRange.cloneRange();
22875         nodeStartRange.collapse(true);
22876     
22877         var nodeEndRange = nodeRange.cloneRange();
22878         nodeEndRange.collapse(false);
22879     
22880         return rangeStartRange.compareBoundaryPoints(
22881                  Range.START_TO_START, nodeEndRange) == -1 &&
22882                rangeEndRange.compareBoundaryPoints(
22883                  Range.START_TO_START, nodeStartRange) == 1;
22884         
22885          
22886     },
22887     rangeCompareNode : function(range, node)
22888     {
22889         var nodeRange = node.ownerDocument.createRange();
22890         try {
22891             nodeRange.selectNode(node);
22892         } catch (e) {
22893             nodeRange.selectNodeContents(node);
22894         }
22895         
22896         
22897         range.collapse(true);
22898     
22899         nodeRange.collapse(true);
22900      
22901         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22902         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22903          
22904         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22905         
22906         var nodeIsBefore   =  ss == 1;
22907         var nodeIsAfter    = ee == -1;
22908         
22909         if (nodeIsBefore && nodeIsAfter) {
22910             return 0; // outer
22911         }
22912         if (!nodeIsBefore && nodeIsAfter) {
22913             return 1; //right trailed.
22914         }
22915         
22916         if (nodeIsBefore && !nodeIsAfter) {
22917             return 2;  // left trailed.
22918         }
22919         // fully contined.
22920         return 3;
22921     },
22922
22923     // private? - in a new class?
22924     cleanUpPaste :  function()
22925     {
22926         // cleans up the whole document..
22927         Roo.log('cleanuppaste');
22928         
22929         this.cleanUpChildren(this.doc.body);
22930         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22931         if (clean != this.doc.body.innerHTML) {
22932             this.doc.body.innerHTML = clean;
22933         }
22934         
22935     },
22936     
22937     cleanWordChars : function(input) {// change the chars to hex code
22938         var he = Roo.HtmlEditorCore;
22939         
22940         var output = input;
22941         Roo.each(he.swapCodes, function(sw) { 
22942             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22943             
22944             output = output.replace(swapper, sw[1]);
22945         });
22946         
22947         return output;
22948     },
22949     
22950     
22951     cleanUpChildren : function (n)
22952     {
22953         if (!n.childNodes.length) {
22954             return;
22955         }
22956         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22957            this.cleanUpChild(n.childNodes[i]);
22958         }
22959     },
22960     
22961     
22962         
22963     
22964     cleanUpChild : function (node)
22965     {
22966         var ed = this;
22967         //console.log(node);
22968         if (node.nodeName == "#text") {
22969             // clean up silly Windows -- stuff?
22970             return; 
22971         }
22972         if (node.nodeName == "#comment") {
22973             node.parentNode.removeChild(node);
22974             // clean up silly Windows -- stuff?
22975             return; 
22976         }
22977         var lcname = node.tagName.toLowerCase();
22978         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22979         // whitelist of tags..
22980         
22981         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22982             // remove node.
22983             node.parentNode.removeChild(node);
22984             return;
22985             
22986         }
22987         
22988         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22989         
22990         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22991         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22992         
22993         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22994         //    remove_keep_children = true;
22995         //}
22996         
22997         if (remove_keep_children) {
22998             this.cleanUpChildren(node);
22999             // inserts everything just before this node...
23000             while (node.childNodes.length) {
23001                 var cn = node.childNodes[0];
23002                 node.removeChild(cn);
23003                 node.parentNode.insertBefore(cn, node);
23004             }
23005             node.parentNode.removeChild(node);
23006             return;
23007         }
23008         
23009         if (!node.attributes || !node.attributes.length) {
23010             this.cleanUpChildren(node);
23011             return;
23012         }
23013         
23014         function cleanAttr(n,v)
23015         {
23016             
23017             if (v.match(/^\./) || v.match(/^\//)) {
23018                 return;
23019             }
23020             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23021                 return;
23022             }
23023             if (v.match(/^#/)) {
23024                 return;
23025             }
23026 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23027             node.removeAttribute(n);
23028             
23029         }
23030         
23031         var cwhite = this.cwhite;
23032         var cblack = this.cblack;
23033             
23034         function cleanStyle(n,v)
23035         {
23036             if (v.match(/expression/)) { //XSS?? should we even bother..
23037                 node.removeAttribute(n);
23038                 return;
23039             }
23040             
23041             var parts = v.split(/;/);
23042             var clean = [];
23043             
23044             Roo.each(parts, function(p) {
23045                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23046                 if (!p.length) {
23047                     return true;
23048                 }
23049                 var l = p.split(':').shift().replace(/\s+/g,'');
23050                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23051                 
23052                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23053 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23054                     //node.removeAttribute(n);
23055                     return true;
23056                 }
23057                 //Roo.log()
23058                 // only allow 'c whitelisted system attributes'
23059                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23060 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23061                     //node.removeAttribute(n);
23062                     return true;
23063                 }
23064                 
23065                 
23066                  
23067                 
23068                 clean.push(p);
23069                 return true;
23070             });
23071             if (clean.length) { 
23072                 node.setAttribute(n, clean.join(';'));
23073             } else {
23074                 node.removeAttribute(n);
23075             }
23076             
23077         }
23078         
23079         
23080         for (var i = node.attributes.length-1; i > -1 ; i--) {
23081             var a = node.attributes[i];
23082             //console.log(a);
23083             
23084             if (a.name.toLowerCase().substr(0,2)=='on')  {
23085                 node.removeAttribute(a.name);
23086                 continue;
23087             }
23088             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23089                 node.removeAttribute(a.name);
23090                 continue;
23091             }
23092             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23093                 cleanAttr(a.name,a.value); // fixme..
23094                 continue;
23095             }
23096             if (a.name == 'style') {
23097                 cleanStyle(a.name,a.value);
23098                 continue;
23099             }
23100             /// clean up MS crap..
23101             // tecnically this should be a list of valid class'es..
23102             
23103             
23104             if (a.name == 'class') {
23105                 if (a.value.match(/^Mso/)) {
23106                     node.className = '';
23107                 }
23108                 
23109                 if (a.value.match(/^body$/)) {
23110                     node.className = '';
23111                 }
23112                 continue;
23113             }
23114             
23115             // style cleanup!?
23116             // class cleanup?
23117             
23118         }
23119         
23120         
23121         this.cleanUpChildren(node);
23122         
23123         
23124     },
23125     
23126     /**
23127      * Clean up MS wordisms...
23128      */
23129     cleanWord : function(node)
23130     {
23131         
23132         
23133         if (!node) {
23134             this.cleanWord(this.doc.body);
23135             return;
23136         }
23137         if (node.nodeName == "#text") {
23138             // clean up silly Windows -- stuff?
23139             return; 
23140         }
23141         if (node.nodeName == "#comment") {
23142             node.parentNode.removeChild(node);
23143             // clean up silly Windows -- stuff?
23144             return; 
23145         }
23146         
23147         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23148             node.parentNode.removeChild(node);
23149             return;
23150         }
23151         
23152         // remove - but keep children..
23153         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23154             while (node.childNodes.length) {
23155                 var cn = node.childNodes[0];
23156                 node.removeChild(cn);
23157                 node.parentNode.insertBefore(cn, node);
23158             }
23159             node.parentNode.removeChild(node);
23160             this.iterateChildren(node, this.cleanWord);
23161             return;
23162         }
23163         // clean styles
23164         if (node.className.length) {
23165             
23166             var cn = node.className.split(/\W+/);
23167             var cna = [];
23168             Roo.each(cn, function(cls) {
23169                 if (cls.match(/Mso[a-zA-Z]+/)) {
23170                     return;
23171                 }
23172                 cna.push(cls);
23173             });
23174             node.className = cna.length ? cna.join(' ') : '';
23175             if (!cna.length) {
23176                 node.removeAttribute("class");
23177             }
23178         }
23179         
23180         if (node.hasAttribute("lang")) {
23181             node.removeAttribute("lang");
23182         }
23183         
23184         if (node.hasAttribute("style")) {
23185             
23186             var styles = node.getAttribute("style").split(";");
23187             var nstyle = [];
23188             Roo.each(styles, function(s) {
23189                 if (!s.match(/:/)) {
23190                     return;
23191                 }
23192                 var kv = s.split(":");
23193                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23194                     return;
23195                 }
23196                 // what ever is left... we allow.
23197                 nstyle.push(s);
23198             });
23199             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23200             if (!nstyle.length) {
23201                 node.removeAttribute('style');
23202             }
23203         }
23204         this.iterateChildren(node, this.cleanWord);
23205         
23206         
23207         
23208     },
23209     /**
23210      * iterateChildren of a Node, calling fn each time, using this as the scole..
23211      * @param {DomNode} node node to iterate children of.
23212      * @param {Function} fn method of this class to call on each item.
23213      */
23214     iterateChildren : function(node, fn)
23215     {
23216         if (!node.childNodes.length) {
23217                 return;
23218         }
23219         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23220            fn.call(this, node.childNodes[i])
23221         }
23222     },
23223     
23224     
23225     /**
23226      * cleanTableWidths.
23227      *
23228      * Quite often pasting from word etc.. results in tables with column and widths.
23229      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23230      *
23231      */
23232     cleanTableWidths : function(node)
23233     {
23234          
23235          
23236         if (!node) {
23237             this.cleanTableWidths(this.doc.body);
23238             return;
23239         }
23240         
23241         // ignore list...
23242         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23243             return; 
23244         }
23245         Roo.log(node.tagName);
23246         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23247             this.iterateChildren(node, this.cleanTableWidths);
23248             return;
23249         }
23250         if (node.hasAttribute('width')) {
23251             node.removeAttribute('width');
23252         }
23253         
23254          
23255         if (node.hasAttribute("style")) {
23256             // pretty basic...
23257             
23258             var styles = node.getAttribute("style").split(";");
23259             var nstyle = [];
23260             Roo.each(styles, function(s) {
23261                 if (!s.match(/:/)) {
23262                     return;
23263                 }
23264                 var kv = s.split(":");
23265                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23266                     return;
23267                 }
23268                 // what ever is left... we allow.
23269                 nstyle.push(s);
23270             });
23271             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23272             if (!nstyle.length) {
23273                 node.removeAttribute('style');
23274             }
23275         }
23276         
23277         this.iterateChildren(node, this.cleanTableWidths);
23278         
23279         
23280     },
23281     
23282     
23283     
23284     
23285     domToHTML : function(currentElement, depth, nopadtext) {
23286         
23287         depth = depth || 0;
23288         nopadtext = nopadtext || false;
23289     
23290         if (!currentElement) {
23291             return this.domToHTML(this.doc.body);
23292         }
23293         
23294         //Roo.log(currentElement);
23295         var j;
23296         var allText = false;
23297         var nodeName = currentElement.nodeName;
23298         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23299         
23300         if  (nodeName == '#text') {
23301             
23302             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23303         }
23304         
23305         
23306         var ret = '';
23307         if (nodeName != 'BODY') {
23308              
23309             var i = 0;
23310             // Prints the node tagName, such as <A>, <IMG>, etc
23311             if (tagName) {
23312                 var attr = [];
23313                 for(i = 0; i < currentElement.attributes.length;i++) {
23314                     // quoting?
23315                     var aname = currentElement.attributes.item(i).name;
23316                     if (!currentElement.attributes.item(i).value.length) {
23317                         continue;
23318                     }
23319                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23320                 }
23321                 
23322                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23323             } 
23324             else {
23325                 
23326                 // eack
23327             }
23328         } else {
23329             tagName = false;
23330         }
23331         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23332             return ret;
23333         }
23334         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23335             nopadtext = true;
23336         }
23337         
23338         
23339         // Traverse the tree
23340         i = 0;
23341         var currentElementChild = currentElement.childNodes.item(i);
23342         var allText = true;
23343         var innerHTML  = '';
23344         lastnode = '';
23345         while (currentElementChild) {
23346             // Formatting code (indent the tree so it looks nice on the screen)
23347             var nopad = nopadtext;
23348             if (lastnode == 'SPAN') {
23349                 nopad  = true;
23350             }
23351             // text
23352             if  (currentElementChild.nodeName == '#text') {
23353                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23354                 toadd = nopadtext ? toadd : toadd.trim();
23355                 if (!nopad && toadd.length > 80) {
23356                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23357                 }
23358                 innerHTML  += toadd;
23359                 
23360                 i++;
23361                 currentElementChild = currentElement.childNodes.item(i);
23362                 lastNode = '';
23363                 continue;
23364             }
23365             allText = false;
23366             
23367             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23368                 
23369             // Recursively traverse the tree structure of the child node
23370             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23371             lastnode = currentElementChild.nodeName;
23372             i++;
23373             currentElementChild=currentElement.childNodes.item(i);
23374         }
23375         
23376         ret += innerHTML;
23377         
23378         if (!allText) {
23379                 // The remaining code is mostly for formatting the tree
23380             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23381         }
23382         
23383         
23384         if (tagName) {
23385             ret+= "</"+tagName+">";
23386         }
23387         return ret;
23388         
23389     },
23390         
23391     applyBlacklists : function()
23392     {
23393         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23394         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23395         
23396         this.white = [];
23397         this.black = [];
23398         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23399             if (b.indexOf(tag) > -1) {
23400                 return;
23401             }
23402             this.white.push(tag);
23403             
23404         }, this);
23405         
23406         Roo.each(w, function(tag) {
23407             if (b.indexOf(tag) > -1) {
23408                 return;
23409             }
23410             if (this.white.indexOf(tag) > -1) {
23411                 return;
23412             }
23413             this.white.push(tag);
23414             
23415         }, this);
23416         
23417         
23418         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23419             if (w.indexOf(tag) > -1) {
23420                 return;
23421             }
23422             this.black.push(tag);
23423             
23424         }, this);
23425         
23426         Roo.each(b, function(tag) {
23427             if (w.indexOf(tag) > -1) {
23428                 return;
23429             }
23430             if (this.black.indexOf(tag) > -1) {
23431                 return;
23432             }
23433             this.black.push(tag);
23434             
23435         }, this);
23436         
23437         
23438         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23439         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23440         
23441         this.cwhite = [];
23442         this.cblack = [];
23443         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23444             if (b.indexOf(tag) > -1) {
23445                 return;
23446             }
23447             this.cwhite.push(tag);
23448             
23449         }, this);
23450         
23451         Roo.each(w, function(tag) {
23452             if (b.indexOf(tag) > -1) {
23453                 return;
23454             }
23455             if (this.cwhite.indexOf(tag) > -1) {
23456                 return;
23457             }
23458             this.cwhite.push(tag);
23459             
23460         }, this);
23461         
23462         
23463         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23464             if (w.indexOf(tag) > -1) {
23465                 return;
23466             }
23467             this.cblack.push(tag);
23468             
23469         }, this);
23470         
23471         Roo.each(b, function(tag) {
23472             if (w.indexOf(tag) > -1) {
23473                 return;
23474             }
23475             if (this.cblack.indexOf(tag) > -1) {
23476                 return;
23477             }
23478             this.cblack.push(tag);
23479             
23480         }, this);
23481     },
23482     
23483     setStylesheets : function(stylesheets)
23484     {
23485         if(typeof(stylesheets) == 'string'){
23486             Roo.get(this.iframe.contentDocument.head).createChild({
23487                 tag : 'link',
23488                 rel : 'stylesheet',
23489                 type : 'text/css',
23490                 href : stylesheets
23491             });
23492             
23493             return;
23494         }
23495         var _this = this;
23496      
23497         Roo.each(stylesheets, function(s) {
23498             if(!s.length){
23499                 return;
23500             }
23501             
23502             Roo.get(_this.iframe.contentDocument.head).createChild({
23503                 tag : 'link',
23504                 rel : 'stylesheet',
23505                 type : 'text/css',
23506                 href : s
23507             });
23508         });
23509
23510         
23511     },
23512     
23513     removeStylesheets : function()
23514     {
23515         var _this = this;
23516         
23517         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23518             s.remove();
23519         });
23520     },
23521     
23522     setStyle : function(style)
23523     {
23524         Roo.get(this.iframe.contentDocument.head).createChild({
23525             tag : 'style',
23526             type : 'text/css',
23527             html : style
23528         });
23529
23530         return;
23531     }
23532     
23533     // hide stuff that is not compatible
23534     /**
23535      * @event blur
23536      * @hide
23537      */
23538     /**
23539      * @event change
23540      * @hide
23541      */
23542     /**
23543      * @event focus
23544      * @hide
23545      */
23546     /**
23547      * @event specialkey
23548      * @hide
23549      */
23550     /**
23551      * @cfg {String} fieldClass @hide
23552      */
23553     /**
23554      * @cfg {String} focusClass @hide
23555      */
23556     /**
23557      * @cfg {String} autoCreate @hide
23558      */
23559     /**
23560      * @cfg {String} inputType @hide
23561      */
23562     /**
23563      * @cfg {String} invalidClass @hide
23564      */
23565     /**
23566      * @cfg {String} invalidText @hide
23567      */
23568     /**
23569      * @cfg {String} msgFx @hide
23570      */
23571     /**
23572      * @cfg {String} validateOnBlur @hide
23573      */
23574 });
23575
23576 Roo.HtmlEditorCore.white = [
23577         'area', 'br', 'img', 'input', 'hr', 'wbr',
23578         
23579        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23580        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23581        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23582        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23583        'table',   'ul',         'xmp', 
23584        
23585        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23586       'thead',   'tr', 
23587      
23588       'dir', 'menu', 'ol', 'ul', 'dl',
23589        
23590       'embed',  'object'
23591 ];
23592
23593
23594 Roo.HtmlEditorCore.black = [
23595     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23596         'applet', // 
23597         'base',   'basefont', 'bgsound', 'blink',  'body', 
23598         'frame',  'frameset', 'head',    'html',   'ilayer', 
23599         'iframe', 'layer',  'link',     'meta',    'object',   
23600         'script', 'style' ,'title',  'xml' // clean later..
23601 ];
23602 Roo.HtmlEditorCore.clean = [
23603     'script', 'style', 'title', 'xml'
23604 ];
23605 Roo.HtmlEditorCore.remove = [
23606     'font'
23607 ];
23608 // attributes..
23609
23610 Roo.HtmlEditorCore.ablack = [
23611     'on'
23612 ];
23613     
23614 Roo.HtmlEditorCore.aclean = [ 
23615     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23616 ];
23617
23618 // protocols..
23619 Roo.HtmlEditorCore.pwhite= [
23620         'http',  'https',  'mailto'
23621 ];
23622
23623 // white listed style attributes.
23624 Roo.HtmlEditorCore.cwhite= [
23625       //  'text-align', /// default is to allow most things..
23626       
23627          
23628 //        'font-size'//??
23629 ];
23630
23631 // black listed style attributes.
23632 Roo.HtmlEditorCore.cblack= [
23633       //  'font-size' -- this can be set by the project 
23634 ];
23635
23636
23637 Roo.HtmlEditorCore.swapCodes   =[ 
23638     [    8211, "--" ], 
23639     [    8212, "--" ], 
23640     [    8216,  "'" ],  
23641     [    8217, "'" ],  
23642     [    8220, '"' ],  
23643     [    8221, '"' ],  
23644     [    8226, "*" ],  
23645     [    8230, "..." ]
23646 ]; 
23647
23648     /*
23649  * - LGPL
23650  *
23651  * HtmlEditor
23652  * 
23653  */
23654
23655 /**
23656  * @class Roo.bootstrap.HtmlEditor
23657  * @extends Roo.bootstrap.TextArea
23658  * Bootstrap HtmlEditor class
23659
23660  * @constructor
23661  * Create a new HtmlEditor
23662  * @param {Object} config The config object
23663  */
23664
23665 Roo.bootstrap.HtmlEditor = function(config){
23666     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23667     if (!this.toolbars) {
23668         this.toolbars = [];
23669     }
23670     
23671     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23672     this.addEvents({
23673             /**
23674              * @event initialize
23675              * Fires when the editor is fully initialized (including the iframe)
23676              * @param {HtmlEditor} this
23677              */
23678             initialize: true,
23679             /**
23680              * @event activate
23681              * Fires when the editor is first receives the focus. Any insertion must wait
23682              * until after this event.
23683              * @param {HtmlEditor} this
23684              */
23685             activate: true,
23686              /**
23687              * @event beforesync
23688              * Fires before the textarea is updated with content from the editor iframe. Return false
23689              * to cancel the sync.
23690              * @param {HtmlEditor} this
23691              * @param {String} html
23692              */
23693             beforesync: true,
23694              /**
23695              * @event beforepush
23696              * Fires before the iframe editor is updated with content from the textarea. Return false
23697              * to cancel the push.
23698              * @param {HtmlEditor} this
23699              * @param {String} html
23700              */
23701             beforepush: true,
23702              /**
23703              * @event sync
23704              * Fires when the textarea is updated with content from the editor iframe.
23705              * @param {HtmlEditor} this
23706              * @param {String} html
23707              */
23708             sync: true,
23709              /**
23710              * @event push
23711              * Fires when the iframe editor is updated with content from the textarea.
23712              * @param {HtmlEditor} this
23713              * @param {String} html
23714              */
23715             push: true,
23716              /**
23717              * @event editmodechange
23718              * Fires when the editor switches edit modes
23719              * @param {HtmlEditor} this
23720              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23721              */
23722             editmodechange: true,
23723             /**
23724              * @event editorevent
23725              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23726              * @param {HtmlEditor} this
23727              */
23728             editorevent: true,
23729             /**
23730              * @event firstfocus
23731              * Fires when on first focus - needed by toolbars..
23732              * @param {HtmlEditor} this
23733              */
23734             firstfocus: true,
23735             /**
23736              * @event autosave
23737              * Auto save the htmlEditor value as a file into Events
23738              * @param {HtmlEditor} this
23739              */
23740             autosave: true,
23741             /**
23742              * @event savedpreview
23743              * preview the saved version of htmlEditor
23744              * @param {HtmlEditor} this
23745              */
23746             savedpreview: true
23747         });
23748 };
23749
23750
23751 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23752     
23753     
23754       /**
23755      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23756      */
23757     toolbars : false,
23758     
23759      /**
23760     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23761     */
23762     btns : [],
23763    
23764      /**
23765      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23766      *                        Roo.resizable.
23767      */
23768     resizable : false,
23769      /**
23770      * @cfg {Number} height (in pixels)
23771      */   
23772     height: 300,
23773    /**
23774      * @cfg {Number} width (in pixels)
23775      */   
23776     width: false,
23777     
23778     /**
23779      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23780      * 
23781      */
23782     stylesheets: false,
23783     
23784     // id of frame..
23785     frameId: false,
23786     
23787     // private properties
23788     validationEvent : false,
23789     deferHeight: true,
23790     initialized : false,
23791     activated : false,
23792     
23793     onFocus : Roo.emptyFn,
23794     iframePad:3,
23795     hideMode:'offsets',
23796     
23797     tbContainer : false,
23798     
23799     bodyCls : '',
23800     
23801     toolbarContainer :function() {
23802         return this.wrap.select('.x-html-editor-tb',true).first();
23803     },
23804
23805     /**
23806      * Protected method that will not generally be called directly. It
23807      * is called when the editor creates its toolbar. Override this method if you need to
23808      * add custom toolbar buttons.
23809      * @param {HtmlEditor} editor
23810      */
23811     createToolbar : function(){
23812         Roo.log('renewing');
23813         Roo.log("create toolbars");
23814         
23815         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23816         this.toolbars[0].render(this.toolbarContainer());
23817         
23818         return;
23819         
23820 //        if (!editor.toolbars || !editor.toolbars.length) {
23821 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23822 //        }
23823 //        
23824 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23825 //            editor.toolbars[i] = Roo.factory(
23826 //                    typeof(editor.toolbars[i]) == 'string' ?
23827 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23828 //                Roo.bootstrap.HtmlEditor);
23829 //            editor.toolbars[i].init(editor);
23830 //        }
23831     },
23832
23833      
23834     // private
23835     onRender : function(ct, position)
23836     {
23837        // Roo.log("Call onRender: " + this.xtype);
23838         var _t = this;
23839         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23840       
23841         this.wrap = this.inputEl().wrap({
23842             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23843         });
23844         
23845         this.editorcore.onRender(ct, position);
23846          
23847         if (this.resizable) {
23848             this.resizeEl = new Roo.Resizable(this.wrap, {
23849                 pinned : true,
23850                 wrap: true,
23851                 dynamic : true,
23852                 minHeight : this.height,
23853                 height: this.height,
23854                 handles : this.resizable,
23855                 width: this.width,
23856                 listeners : {
23857                     resize : function(r, w, h) {
23858                         _t.onResize(w,h); // -something
23859                     }
23860                 }
23861             });
23862             
23863         }
23864         this.createToolbar(this);
23865        
23866         
23867         if(!this.width && this.resizable){
23868             this.setSize(this.wrap.getSize());
23869         }
23870         if (this.resizeEl) {
23871             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23872             // should trigger onReize..
23873         }
23874         
23875     },
23876
23877     // private
23878     onResize : function(w, h)
23879     {
23880         Roo.log('resize: ' +w + ',' + h );
23881         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23882         var ew = false;
23883         var eh = false;
23884         
23885         if(this.inputEl() ){
23886             if(typeof w == 'number'){
23887                 var aw = w - this.wrap.getFrameWidth('lr');
23888                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23889                 ew = aw;
23890             }
23891             if(typeof h == 'number'){
23892                  var tbh = -11;  // fixme it needs to tool bar size!
23893                 for (var i =0; i < this.toolbars.length;i++) {
23894                     // fixme - ask toolbars for heights?
23895                     tbh += this.toolbars[i].el.getHeight();
23896                     //if (this.toolbars[i].footer) {
23897                     //    tbh += this.toolbars[i].footer.el.getHeight();
23898                     //}
23899                 }
23900               
23901                 
23902                 
23903                 
23904                 
23905                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23906                 ah -= 5; // knock a few pixes off for look..
23907                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23908                 var eh = ah;
23909             }
23910         }
23911         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23912         this.editorcore.onResize(ew,eh);
23913         
23914     },
23915
23916     /**
23917      * Toggles the editor between standard and source edit mode.
23918      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23919      */
23920     toggleSourceEdit : function(sourceEditMode)
23921     {
23922         this.editorcore.toggleSourceEdit(sourceEditMode);
23923         
23924         if(this.editorcore.sourceEditMode){
23925             Roo.log('editor - showing textarea');
23926             
23927 //            Roo.log('in');
23928 //            Roo.log(this.syncValue());
23929             this.syncValue();
23930             this.inputEl().removeClass(['hide', 'x-hidden']);
23931             this.inputEl().dom.removeAttribute('tabIndex');
23932             this.inputEl().focus();
23933         }else{
23934             Roo.log('editor - hiding textarea');
23935 //            Roo.log('out')
23936 //            Roo.log(this.pushValue()); 
23937             this.pushValue();
23938             
23939             this.inputEl().addClass(['hide', 'x-hidden']);
23940             this.inputEl().dom.setAttribute('tabIndex', -1);
23941             //this.deferFocus();
23942         }
23943          
23944         if(this.resizable){
23945             this.setSize(this.wrap.getSize());
23946         }
23947         
23948         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23949     },
23950  
23951     // private (for BoxComponent)
23952     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23953
23954     // private (for BoxComponent)
23955     getResizeEl : function(){
23956         return this.wrap;
23957     },
23958
23959     // private (for BoxComponent)
23960     getPositionEl : function(){
23961         return this.wrap;
23962     },
23963
23964     // private
23965     initEvents : function(){
23966         this.originalValue = this.getValue();
23967     },
23968
23969 //    /**
23970 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23971 //     * @method
23972 //     */
23973 //    markInvalid : Roo.emptyFn,
23974 //    /**
23975 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23976 //     * @method
23977 //     */
23978 //    clearInvalid : Roo.emptyFn,
23979
23980     setValue : function(v){
23981         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23982         this.editorcore.pushValue();
23983     },
23984
23985      
23986     // private
23987     deferFocus : function(){
23988         this.focus.defer(10, this);
23989     },
23990
23991     // doc'ed in Field
23992     focus : function(){
23993         this.editorcore.focus();
23994         
23995     },
23996       
23997
23998     // private
23999     onDestroy : function(){
24000         
24001         
24002         
24003         if(this.rendered){
24004             
24005             for (var i =0; i < this.toolbars.length;i++) {
24006                 // fixme - ask toolbars for heights?
24007                 this.toolbars[i].onDestroy();
24008             }
24009             
24010             this.wrap.dom.innerHTML = '';
24011             this.wrap.remove();
24012         }
24013     },
24014
24015     // private
24016     onFirstFocus : function(){
24017         //Roo.log("onFirstFocus");
24018         this.editorcore.onFirstFocus();
24019          for (var i =0; i < this.toolbars.length;i++) {
24020             this.toolbars[i].onFirstFocus();
24021         }
24022         
24023     },
24024     
24025     // private
24026     syncValue : function()
24027     {   
24028         this.editorcore.syncValue();
24029     },
24030     
24031     pushValue : function()
24032     {   
24033         this.editorcore.pushValue();
24034     }
24035      
24036     
24037     // hide stuff that is not compatible
24038     /**
24039      * @event blur
24040      * @hide
24041      */
24042     /**
24043      * @event change
24044      * @hide
24045      */
24046     /**
24047      * @event focus
24048      * @hide
24049      */
24050     /**
24051      * @event specialkey
24052      * @hide
24053      */
24054     /**
24055      * @cfg {String} fieldClass @hide
24056      */
24057     /**
24058      * @cfg {String} focusClass @hide
24059      */
24060     /**
24061      * @cfg {String} autoCreate @hide
24062      */
24063     /**
24064      * @cfg {String} inputType @hide
24065      */
24066      
24067     /**
24068      * @cfg {String} invalidText @hide
24069      */
24070     /**
24071      * @cfg {String} msgFx @hide
24072      */
24073     /**
24074      * @cfg {String} validateOnBlur @hide
24075      */
24076 });
24077  
24078     
24079    
24080    
24081    
24082       
24083 Roo.namespace('Roo.bootstrap.htmleditor');
24084 /**
24085  * @class Roo.bootstrap.HtmlEditorToolbar1
24086  * Basic Toolbar
24087  * 
24088  * @example
24089  * Usage:
24090  *
24091  new Roo.bootstrap.HtmlEditor({
24092     ....
24093     toolbars : [
24094         new Roo.bootstrap.HtmlEditorToolbar1({
24095             disable : { fonts: 1 , format: 1, ..., ... , ...],
24096             btns : [ .... ]
24097         })
24098     }
24099      
24100  * 
24101  * @cfg {Object} disable List of elements to disable..
24102  * @cfg {Array} btns List of additional buttons.
24103  * 
24104  * 
24105  * NEEDS Extra CSS? 
24106  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24107  */
24108  
24109 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24110 {
24111     
24112     Roo.apply(this, config);
24113     
24114     // default disabled, based on 'good practice'..
24115     this.disable = this.disable || {};
24116     Roo.applyIf(this.disable, {
24117         fontSize : true,
24118         colors : true,
24119         specialElements : true
24120     });
24121     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24122     
24123     this.editor = config.editor;
24124     this.editorcore = config.editor.editorcore;
24125     
24126     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24127     
24128     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24129     // dont call parent... till later.
24130 }
24131 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24132      
24133     bar : true,
24134     
24135     editor : false,
24136     editorcore : false,
24137     
24138     
24139     formats : [
24140         "p" ,  
24141         "h1","h2","h3","h4","h5","h6", 
24142         "pre", "code", 
24143         "abbr", "acronym", "address", "cite", "samp", "var",
24144         'div','span'
24145     ],
24146     
24147     onRender : function(ct, position)
24148     {
24149        // Roo.log("Call onRender: " + this.xtype);
24150         
24151        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24152        Roo.log(this.el);
24153        this.el.dom.style.marginBottom = '0';
24154        var _this = this;
24155        var editorcore = this.editorcore;
24156        var editor= this.editor;
24157        
24158        var children = [];
24159        var btn = function(id,cmd , toggle, handler, html){
24160        
24161             var  event = toggle ? 'toggle' : 'click';
24162        
24163             var a = {
24164                 size : 'sm',
24165                 xtype: 'Button',
24166                 xns: Roo.bootstrap,
24167                 //glyphicon : id,
24168                 fa: id,
24169                 cmd : id || cmd,
24170                 enableToggle:toggle !== false,
24171                 html : html || '',
24172                 pressed : toggle ? false : null,
24173                 listeners : {}
24174             };
24175             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24176                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24177             };
24178             children.push(a);
24179             return a;
24180        }
24181        
24182     //    var cb_box = function...
24183         
24184         var style = {
24185                 xtype: 'Button',
24186                 size : 'sm',
24187                 xns: Roo.bootstrap,
24188                 fa : 'font',
24189                 //html : 'submit'
24190                 menu : {
24191                     xtype: 'Menu',
24192                     xns: Roo.bootstrap,
24193                     items:  []
24194                 }
24195         };
24196         Roo.each(this.formats, function(f) {
24197             style.menu.items.push({
24198                 xtype :'MenuItem',
24199                 xns: Roo.bootstrap,
24200                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24201                 tagname : f,
24202                 listeners : {
24203                     click : function()
24204                     {
24205                         editorcore.insertTag(this.tagname);
24206                         editor.focus();
24207                     }
24208                 }
24209                 
24210             });
24211         });
24212         children.push(style);   
24213         
24214         btn('bold',false,true);
24215         btn('italic',false,true);
24216         btn('align-left', 'justifyleft',true);
24217         btn('align-center', 'justifycenter',true);
24218         btn('align-right' , 'justifyright',true);
24219         btn('link', false, false, function(btn) {
24220             //Roo.log("create link?");
24221             var url = prompt(this.createLinkText, this.defaultLinkValue);
24222             if(url && url != 'http:/'+'/'){
24223                 this.editorcore.relayCmd('createlink', url);
24224             }
24225         }),
24226         btn('list','insertunorderedlist',true);
24227         btn('pencil', false,true, function(btn){
24228                 Roo.log(this);
24229                 this.toggleSourceEdit(btn.pressed);
24230         });
24231         
24232         if (this.editor.btns.length > 0) {
24233             for (var i = 0; i<this.editor.btns.length; i++) {
24234                 children.push(this.editor.btns[i]);
24235             }
24236         }
24237         
24238         /*
24239         var cog = {
24240                 xtype: 'Button',
24241                 size : 'sm',
24242                 xns: Roo.bootstrap,
24243                 glyphicon : 'cog',
24244                 //html : 'submit'
24245                 menu : {
24246                     xtype: 'Menu',
24247                     xns: Roo.bootstrap,
24248                     items:  []
24249                 }
24250         };
24251         
24252         cog.menu.items.push({
24253             xtype :'MenuItem',
24254             xns: Roo.bootstrap,
24255             html : Clean styles,
24256             tagname : f,
24257             listeners : {
24258                 click : function()
24259                 {
24260                     editorcore.insertTag(this.tagname);
24261                     editor.focus();
24262                 }
24263             }
24264             
24265         });
24266        */
24267         
24268          
24269        this.xtype = 'NavSimplebar';
24270         
24271         for(var i=0;i< children.length;i++) {
24272             
24273             this.buttons.add(this.addxtypeChild(children[i]));
24274             
24275         }
24276         
24277         editor.on('editorevent', this.updateToolbar, this);
24278     },
24279     onBtnClick : function(id)
24280     {
24281        this.editorcore.relayCmd(id);
24282        this.editorcore.focus();
24283     },
24284     
24285     /**
24286      * Protected method that will not generally be called directly. It triggers
24287      * a toolbar update by reading the markup state of the current selection in the editor.
24288      */
24289     updateToolbar: function(){
24290
24291         if(!this.editorcore.activated){
24292             this.editor.onFirstFocus(); // is this neeed?
24293             return;
24294         }
24295
24296         var btns = this.buttons; 
24297         var doc = this.editorcore.doc;
24298         btns.get('bold').setActive(doc.queryCommandState('bold'));
24299         btns.get('italic').setActive(doc.queryCommandState('italic'));
24300         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24301         
24302         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24303         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24304         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24305         
24306         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24307         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24308          /*
24309         
24310         var ans = this.editorcore.getAllAncestors();
24311         if (this.formatCombo) {
24312             
24313             
24314             var store = this.formatCombo.store;
24315             this.formatCombo.setValue("");
24316             for (var i =0; i < ans.length;i++) {
24317                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24318                     // select it..
24319                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24320                     break;
24321                 }
24322             }
24323         }
24324         
24325         
24326         
24327         // hides menus... - so this cant be on a menu...
24328         Roo.bootstrap.MenuMgr.hideAll();
24329         */
24330         Roo.bootstrap.MenuMgr.hideAll();
24331         //this.editorsyncValue();
24332     },
24333     onFirstFocus: function() {
24334         this.buttons.each(function(item){
24335            item.enable();
24336         });
24337     },
24338     toggleSourceEdit : function(sourceEditMode){
24339         
24340           
24341         if(sourceEditMode){
24342             Roo.log("disabling buttons");
24343            this.buttons.each( function(item){
24344                 if(item.cmd != 'pencil'){
24345                     item.disable();
24346                 }
24347             });
24348           
24349         }else{
24350             Roo.log("enabling buttons");
24351             if(this.editorcore.initialized){
24352                 this.buttons.each( function(item){
24353                     item.enable();
24354                 });
24355             }
24356             
24357         }
24358         Roo.log("calling toggole on editor");
24359         // tell the editor that it's been pressed..
24360         this.editor.toggleSourceEdit(sourceEditMode);
24361        
24362     }
24363 });
24364
24365
24366
24367
24368
24369 /**
24370  * @class Roo.bootstrap.Table.AbstractSelectionModel
24371  * @extends Roo.util.Observable
24372  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24373  * implemented by descendant classes.  This class should not be directly instantiated.
24374  * @constructor
24375  */
24376 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24377     this.locked = false;
24378     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24379 };
24380
24381
24382 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24383     /** @ignore Called by the grid automatically. Do not call directly. */
24384     init : function(grid){
24385         this.grid = grid;
24386         this.initEvents();
24387     },
24388
24389     /**
24390      * Locks the selections.
24391      */
24392     lock : function(){
24393         this.locked = true;
24394     },
24395
24396     /**
24397      * Unlocks the selections.
24398      */
24399     unlock : function(){
24400         this.locked = false;
24401     },
24402
24403     /**
24404      * Returns true if the selections are locked.
24405      * @return {Boolean}
24406      */
24407     isLocked : function(){
24408         return this.locked;
24409     }
24410 });
24411 /**
24412  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24413  * @class Roo.bootstrap.Table.RowSelectionModel
24414  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24415  * It supports multiple selections and keyboard selection/navigation. 
24416  * @constructor
24417  * @param {Object} config
24418  */
24419
24420 Roo.bootstrap.Table.RowSelectionModel = function(config){
24421     Roo.apply(this, config);
24422     this.selections = new Roo.util.MixedCollection(false, function(o){
24423         return o.id;
24424     });
24425
24426     this.last = false;
24427     this.lastActive = false;
24428
24429     this.addEvents({
24430         /**
24431              * @event selectionchange
24432              * Fires when the selection changes
24433              * @param {SelectionModel} this
24434              */
24435             "selectionchange" : true,
24436         /**
24437              * @event afterselectionchange
24438              * Fires after the selection changes (eg. by key press or clicking)
24439              * @param {SelectionModel} this
24440              */
24441             "afterselectionchange" : true,
24442         /**
24443              * @event beforerowselect
24444              * Fires when a row is selected being selected, return false to cancel.
24445              * @param {SelectionModel} this
24446              * @param {Number} rowIndex The selected index
24447              * @param {Boolean} keepExisting False if other selections will be cleared
24448              */
24449             "beforerowselect" : true,
24450         /**
24451              * @event rowselect
24452              * Fires when a row is selected.
24453              * @param {SelectionModel} this
24454              * @param {Number} rowIndex The selected index
24455              * @param {Roo.data.Record} r The record
24456              */
24457             "rowselect" : true,
24458         /**
24459              * @event rowdeselect
24460              * Fires when a row is deselected.
24461              * @param {SelectionModel} this
24462              * @param {Number} rowIndex The selected index
24463              */
24464         "rowdeselect" : true
24465     });
24466     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24467     this.locked = false;
24468  };
24469
24470 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24471     /**
24472      * @cfg {Boolean} singleSelect
24473      * True to allow selection of only one row at a time (defaults to false)
24474      */
24475     singleSelect : false,
24476
24477     // private
24478     initEvents : function()
24479     {
24480
24481         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24482         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24483         //}else{ // allow click to work like normal
24484          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24485         //}
24486         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24487         this.grid.on("rowclick", this.handleMouseDown, this);
24488         
24489         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24490             "up" : function(e){
24491                 if(!e.shiftKey){
24492                     this.selectPrevious(e.shiftKey);
24493                 }else if(this.last !== false && this.lastActive !== false){
24494                     var last = this.last;
24495                     this.selectRange(this.last,  this.lastActive-1);
24496                     this.grid.getView().focusRow(this.lastActive);
24497                     if(last !== false){
24498                         this.last = last;
24499                     }
24500                 }else{
24501                     this.selectFirstRow();
24502                 }
24503                 this.fireEvent("afterselectionchange", this);
24504             },
24505             "down" : function(e){
24506                 if(!e.shiftKey){
24507                     this.selectNext(e.shiftKey);
24508                 }else if(this.last !== false && this.lastActive !== false){
24509                     var last = this.last;
24510                     this.selectRange(this.last,  this.lastActive+1);
24511                     this.grid.getView().focusRow(this.lastActive);
24512                     if(last !== false){
24513                         this.last = last;
24514                     }
24515                 }else{
24516                     this.selectFirstRow();
24517                 }
24518                 this.fireEvent("afterselectionchange", this);
24519             },
24520             scope: this
24521         });
24522         this.grid.store.on('load', function(){
24523             this.selections.clear();
24524         },this);
24525         /*
24526         var view = this.grid.view;
24527         view.on("refresh", this.onRefresh, this);
24528         view.on("rowupdated", this.onRowUpdated, this);
24529         view.on("rowremoved", this.onRemove, this);
24530         */
24531     },
24532
24533     // private
24534     onRefresh : function()
24535     {
24536         var ds = this.grid.store, i, v = this.grid.view;
24537         var s = this.selections;
24538         s.each(function(r){
24539             if((i = ds.indexOfId(r.id)) != -1){
24540                 v.onRowSelect(i);
24541             }else{
24542                 s.remove(r);
24543             }
24544         });
24545     },
24546
24547     // private
24548     onRemove : function(v, index, r){
24549         this.selections.remove(r);
24550     },
24551
24552     // private
24553     onRowUpdated : function(v, index, r){
24554         if(this.isSelected(r)){
24555             v.onRowSelect(index);
24556         }
24557     },
24558
24559     /**
24560      * Select records.
24561      * @param {Array} records The records to select
24562      * @param {Boolean} keepExisting (optional) True to keep existing selections
24563      */
24564     selectRecords : function(records, keepExisting)
24565     {
24566         if(!keepExisting){
24567             this.clearSelections();
24568         }
24569             var ds = this.grid.store;
24570         for(var i = 0, len = records.length; i < len; i++){
24571             this.selectRow(ds.indexOf(records[i]), true);
24572         }
24573     },
24574
24575     /**
24576      * Gets the number of selected rows.
24577      * @return {Number}
24578      */
24579     getCount : function(){
24580         return this.selections.length;
24581     },
24582
24583     /**
24584      * Selects the first row in the grid.
24585      */
24586     selectFirstRow : function(){
24587         this.selectRow(0);
24588     },
24589
24590     /**
24591      * Select the last row.
24592      * @param {Boolean} keepExisting (optional) True to keep existing selections
24593      */
24594     selectLastRow : function(keepExisting){
24595         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24596         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24597     },
24598
24599     /**
24600      * Selects the row immediately following the last selected row.
24601      * @param {Boolean} keepExisting (optional) True to keep existing selections
24602      */
24603     selectNext : function(keepExisting)
24604     {
24605             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24606             this.selectRow(this.last+1, keepExisting);
24607             this.grid.getView().focusRow(this.last);
24608         }
24609     },
24610
24611     /**
24612      * Selects the row that precedes the last selected row.
24613      * @param {Boolean} keepExisting (optional) True to keep existing selections
24614      */
24615     selectPrevious : function(keepExisting){
24616         if(this.last){
24617             this.selectRow(this.last-1, keepExisting);
24618             this.grid.getView().focusRow(this.last);
24619         }
24620     },
24621
24622     /**
24623      * Returns the selected records
24624      * @return {Array} Array of selected records
24625      */
24626     getSelections : function(){
24627         return [].concat(this.selections.items);
24628     },
24629
24630     /**
24631      * Returns the first selected record.
24632      * @return {Record}
24633      */
24634     getSelected : function(){
24635         return this.selections.itemAt(0);
24636     },
24637
24638
24639     /**
24640      * Clears all selections.
24641      */
24642     clearSelections : function(fast)
24643     {
24644         if(this.locked) {
24645             return;
24646         }
24647         if(fast !== true){
24648                 var ds = this.grid.store;
24649             var s = this.selections;
24650             s.each(function(r){
24651                 this.deselectRow(ds.indexOfId(r.id));
24652             }, this);
24653             s.clear();
24654         }else{
24655             this.selections.clear();
24656         }
24657         this.last = false;
24658     },
24659
24660
24661     /**
24662      * Selects all rows.
24663      */
24664     selectAll : function(){
24665         if(this.locked) {
24666             return;
24667         }
24668         this.selections.clear();
24669         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24670             this.selectRow(i, true);
24671         }
24672     },
24673
24674     /**
24675      * Returns True if there is a selection.
24676      * @return {Boolean}
24677      */
24678     hasSelection : function(){
24679         return this.selections.length > 0;
24680     },
24681
24682     /**
24683      * Returns True if the specified row is selected.
24684      * @param {Number/Record} record The record or index of the record to check
24685      * @return {Boolean}
24686      */
24687     isSelected : function(index){
24688             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24689         return (r && this.selections.key(r.id) ? true : false);
24690     },
24691
24692     /**
24693      * Returns True if the specified record id is selected.
24694      * @param {String} id The id of record to check
24695      * @return {Boolean}
24696      */
24697     isIdSelected : function(id){
24698         return (this.selections.key(id) ? true : false);
24699     },
24700
24701
24702     // private
24703     handleMouseDBClick : function(e, t){
24704         
24705     },
24706     // private
24707     handleMouseDown : function(e, t)
24708     {
24709             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24710         if(this.isLocked() || rowIndex < 0 ){
24711             return;
24712         };
24713         if(e.shiftKey && this.last !== false){
24714             var last = this.last;
24715             this.selectRange(last, rowIndex, e.ctrlKey);
24716             this.last = last; // reset the last
24717             t.focus();
24718     
24719         }else{
24720             var isSelected = this.isSelected(rowIndex);
24721             //Roo.log("select row:" + rowIndex);
24722             if(isSelected){
24723                 this.deselectRow(rowIndex);
24724             } else {
24725                         this.selectRow(rowIndex, true);
24726             }
24727     
24728             /*
24729                 if(e.button !== 0 && isSelected){
24730                 alert('rowIndex 2: ' + rowIndex);
24731                     view.focusRow(rowIndex);
24732                 }else if(e.ctrlKey && isSelected){
24733                     this.deselectRow(rowIndex);
24734                 }else if(!isSelected){
24735                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24736                     view.focusRow(rowIndex);
24737                 }
24738             */
24739         }
24740         this.fireEvent("afterselectionchange", this);
24741     },
24742     // private
24743     handleDragableRowClick :  function(grid, rowIndex, e) 
24744     {
24745         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24746             this.selectRow(rowIndex, false);
24747             grid.view.focusRow(rowIndex);
24748              this.fireEvent("afterselectionchange", this);
24749         }
24750     },
24751     
24752     /**
24753      * Selects multiple rows.
24754      * @param {Array} rows Array of the indexes of the row to select
24755      * @param {Boolean} keepExisting (optional) True to keep existing selections
24756      */
24757     selectRows : function(rows, keepExisting){
24758         if(!keepExisting){
24759             this.clearSelections();
24760         }
24761         for(var i = 0, len = rows.length; i < len; i++){
24762             this.selectRow(rows[i], true);
24763         }
24764     },
24765
24766     /**
24767      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24768      * @param {Number} startRow The index of the first row in the range
24769      * @param {Number} endRow The index of the last row in the range
24770      * @param {Boolean} keepExisting (optional) True to retain existing selections
24771      */
24772     selectRange : function(startRow, endRow, keepExisting){
24773         if(this.locked) {
24774             return;
24775         }
24776         if(!keepExisting){
24777             this.clearSelections();
24778         }
24779         if(startRow <= endRow){
24780             for(var i = startRow; i <= endRow; i++){
24781                 this.selectRow(i, true);
24782             }
24783         }else{
24784             for(var i = startRow; i >= endRow; i--){
24785                 this.selectRow(i, true);
24786             }
24787         }
24788     },
24789
24790     /**
24791      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24792      * @param {Number} startRow The index of the first row in the range
24793      * @param {Number} endRow The index of the last row in the range
24794      */
24795     deselectRange : function(startRow, endRow, preventViewNotify){
24796         if(this.locked) {
24797             return;
24798         }
24799         for(var i = startRow; i <= endRow; i++){
24800             this.deselectRow(i, preventViewNotify);
24801         }
24802     },
24803
24804     /**
24805      * Selects a row.
24806      * @param {Number} row The index of the row to select
24807      * @param {Boolean} keepExisting (optional) True to keep existing selections
24808      */
24809     selectRow : function(index, keepExisting, preventViewNotify)
24810     {
24811             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24812             return;
24813         }
24814         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24815             if(!keepExisting || this.singleSelect){
24816                 this.clearSelections();
24817             }
24818             
24819             var r = this.grid.store.getAt(index);
24820             //console.log('selectRow - record id :' + r.id);
24821             
24822             this.selections.add(r);
24823             this.last = this.lastActive = index;
24824             if(!preventViewNotify){
24825                 var proxy = new Roo.Element(
24826                                 this.grid.getRowDom(index)
24827                 );
24828                 proxy.addClass('bg-info info');
24829             }
24830             this.fireEvent("rowselect", this, index, r);
24831             this.fireEvent("selectionchange", this);
24832         }
24833     },
24834
24835     /**
24836      * Deselects a row.
24837      * @param {Number} row The index of the row to deselect
24838      */
24839     deselectRow : function(index, preventViewNotify)
24840     {
24841         if(this.locked) {
24842             return;
24843         }
24844         if(this.last == index){
24845             this.last = false;
24846         }
24847         if(this.lastActive == index){
24848             this.lastActive = false;
24849         }
24850         
24851         var r = this.grid.store.getAt(index);
24852         if (!r) {
24853             return;
24854         }
24855         
24856         this.selections.remove(r);
24857         //.console.log('deselectRow - record id :' + r.id);
24858         if(!preventViewNotify){
24859         
24860             var proxy = new Roo.Element(
24861                 this.grid.getRowDom(index)
24862             );
24863             proxy.removeClass('bg-info info');
24864         }
24865         this.fireEvent("rowdeselect", this, index);
24866         this.fireEvent("selectionchange", this);
24867     },
24868
24869     // private
24870     restoreLast : function(){
24871         if(this._last){
24872             this.last = this._last;
24873         }
24874     },
24875
24876     // private
24877     acceptsNav : function(row, col, cm){
24878         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24879     },
24880
24881     // private
24882     onEditorKey : function(field, e){
24883         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24884         if(k == e.TAB){
24885             e.stopEvent();
24886             ed.completeEdit();
24887             if(e.shiftKey){
24888                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24889             }else{
24890                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24891             }
24892         }else if(k == e.ENTER && !e.ctrlKey){
24893             e.stopEvent();
24894             ed.completeEdit();
24895             if(e.shiftKey){
24896                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24897             }else{
24898                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24899             }
24900         }else if(k == e.ESC){
24901             ed.cancelEdit();
24902         }
24903         if(newCell){
24904             g.startEditing(newCell[0], newCell[1]);
24905         }
24906     }
24907 });
24908 /*
24909  * Based on:
24910  * Ext JS Library 1.1.1
24911  * Copyright(c) 2006-2007, Ext JS, LLC.
24912  *
24913  * Originally Released Under LGPL - original licence link has changed is not relivant.
24914  *
24915  * Fork - LGPL
24916  * <script type="text/javascript">
24917  */
24918  
24919 /**
24920  * @class Roo.bootstrap.PagingToolbar
24921  * @extends Roo.bootstrap.NavSimplebar
24922  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24923  * @constructor
24924  * Create a new PagingToolbar
24925  * @param {Object} config The config object
24926  * @param {Roo.data.Store} store
24927  */
24928 Roo.bootstrap.PagingToolbar = function(config)
24929 {
24930     // old args format still supported... - xtype is prefered..
24931         // created from xtype...
24932     
24933     this.ds = config.dataSource;
24934     
24935     if (config.store && !this.ds) {
24936         this.store= Roo.factory(config.store, Roo.data);
24937         this.ds = this.store;
24938         this.ds.xmodule = this.xmodule || false;
24939     }
24940     
24941     this.toolbarItems = [];
24942     if (config.items) {
24943         this.toolbarItems = config.items;
24944     }
24945     
24946     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24947     
24948     this.cursor = 0;
24949     
24950     if (this.ds) { 
24951         this.bind(this.ds);
24952     }
24953     
24954     if (Roo.bootstrap.version == 4) {
24955         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24956     } else {
24957         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24958     }
24959     
24960 };
24961
24962 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24963     /**
24964      * @cfg {Roo.data.Store} dataSource
24965      * The underlying data store providing the paged data
24966      */
24967     /**
24968      * @cfg {String/HTMLElement/Element} container
24969      * container The id or element that will contain the toolbar
24970      */
24971     /**
24972      * @cfg {Boolean} displayInfo
24973      * True to display the displayMsg (defaults to false)
24974      */
24975     /**
24976      * @cfg {Number} pageSize
24977      * The number of records to display per page (defaults to 20)
24978      */
24979     pageSize: 20,
24980     /**
24981      * @cfg {String} displayMsg
24982      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24983      */
24984     displayMsg : 'Displaying {0} - {1} of {2}',
24985     /**
24986      * @cfg {String} emptyMsg
24987      * The message to display when no records are found (defaults to "No data to display")
24988      */
24989     emptyMsg : 'No data to display',
24990     /**
24991      * Customizable piece of the default paging text (defaults to "Page")
24992      * @type String
24993      */
24994     beforePageText : "Page",
24995     /**
24996      * Customizable piece of the default paging text (defaults to "of %0")
24997      * @type String
24998      */
24999     afterPageText : "of {0}",
25000     /**
25001      * Customizable piece of the default paging text (defaults to "First Page")
25002      * @type String
25003      */
25004     firstText : "First Page",
25005     /**
25006      * Customizable piece of the default paging text (defaults to "Previous Page")
25007      * @type String
25008      */
25009     prevText : "Previous Page",
25010     /**
25011      * Customizable piece of the default paging text (defaults to "Next Page")
25012      * @type String
25013      */
25014     nextText : "Next Page",
25015     /**
25016      * Customizable piece of the default paging text (defaults to "Last Page")
25017      * @type String
25018      */
25019     lastText : "Last Page",
25020     /**
25021      * Customizable piece of the default paging text (defaults to "Refresh")
25022      * @type String
25023      */
25024     refreshText : "Refresh",
25025
25026     buttons : false,
25027     // private
25028     onRender : function(ct, position) 
25029     {
25030         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25031         this.navgroup.parentId = this.id;
25032         this.navgroup.onRender(this.el, null);
25033         // add the buttons to the navgroup
25034         
25035         if(this.displayInfo){
25036             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25037             this.displayEl = this.el.select('.x-paging-info', true).first();
25038 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25039 //            this.displayEl = navel.el.select('span',true).first();
25040         }
25041         
25042         var _this = this;
25043         
25044         if(this.buttons){
25045             Roo.each(_this.buttons, function(e){ // this might need to use render????
25046                Roo.factory(e).render(_this.el);
25047             });
25048         }
25049             
25050         Roo.each(_this.toolbarItems, function(e) {
25051             _this.navgroup.addItem(e);
25052         });
25053         
25054         
25055         this.first = this.navgroup.addItem({
25056             tooltip: this.firstText,
25057             cls: "prev btn-outline-secondary",
25058             html : ' <i class="fa fa-step-backward"></i>',
25059             disabled: true,
25060             preventDefault: true,
25061             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25062         });
25063         
25064         this.prev =  this.navgroup.addItem({
25065             tooltip: this.prevText,
25066             cls: "prev btn-outline-secondary",
25067             html : ' <i class="fa fa-backward"></i>',
25068             disabled: true,
25069             preventDefault: true,
25070             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25071         });
25072     //this.addSeparator();
25073         
25074         
25075         var field = this.navgroup.addItem( {
25076             tagtype : 'span',
25077             cls : 'x-paging-position  btn-outline-secondary',
25078              disabled: true,
25079             html : this.beforePageText  +
25080                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25081                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25082          } ); //?? escaped?
25083         
25084         this.field = field.el.select('input', true).first();
25085         this.field.on("keydown", this.onPagingKeydown, this);
25086         this.field.on("focus", function(){this.dom.select();});
25087     
25088     
25089         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25090         //this.field.setHeight(18);
25091         //this.addSeparator();
25092         this.next = this.navgroup.addItem({
25093             tooltip: this.nextText,
25094             cls: "next btn-outline-secondary",
25095             html : ' <i class="fa fa-forward"></i>',
25096             disabled: true,
25097             preventDefault: true,
25098             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25099         });
25100         this.last = this.navgroup.addItem({
25101             tooltip: this.lastText,
25102             html : ' <i class="fa fa-step-forward"></i>',
25103             cls: "next btn-outline-secondary",
25104             disabled: true,
25105             preventDefault: true,
25106             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25107         });
25108     //this.addSeparator();
25109         this.loading = this.navgroup.addItem({
25110             tooltip: this.refreshText,
25111             cls: "btn-outline-secondary",
25112             html : ' <i class="fa fa-refresh"></i>',
25113             preventDefault: true,
25114             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25115         });
25116         
25117     },
25118
25119     // private
25120     updateInfo : function(){
25121         if(this.displayEl){
25122             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25123             var msg = count == 0 ?
25124                 this.emptyMsg :
25125                 String.format(
25126                     this.displayMsg,
25127                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25128                 );
25129             this.displayEl.update(msg);
25130         }
25131     },
25132
25133     // private
25134     onLoad : function(ds, r, o)
25135     {
25136         this.cursor = o.params.start ? o.params.start : 0;
25137         
25138         var d = this.getPageData(),
25139             ap = d.activePage,
25140             ps = d.pages;
25141         
25142         
25143         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25144         this.field.dom.value = ap;
25145         this.first.setDisabled(ap == 1);
25146         this.prev.setDisabled(ap == 1);
25147         this.next.setDisabled(ap == ps);
25148         this.last.setDisabled(ap == ps);
25149         this.loading.enable();
25150         this.updateInfo();
25151     },
25152
25153     // private
25154     getPageData : function(){
25155         var total = this.ds.getTotalCount();
25156         return {
25157             total : total,
25158             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25159             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25160         };
25161     },
25162
25163     // private
25164     onLoadError : function(){
25165         this.loading.enable();
25166     },
25167
25168     // private
25169     onPagingKeydown : function(e){
25170         var k = e.getKey();
25171         var d = this.getPageData();
25172         if(k == e.RETURN){
25173             var v = this.field.dom.value, pageNum;
25174             if(!v || isNaN(pageNum = parseInt(v, 10))){
25175                 this.field.dom.value = d.activePage;
25176                 return;
25177             }
25178             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25179             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25180             e.stopEvent();
25181         }
25182         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))
25183         {
25184           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25185           this.field.dom.value = pageNum;
25186           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25187           e.stopEvent();
25188         }
25189         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25190         {
25191           var v = this.field.dom.value, pageNum; 
25192           var increment = (e.shiftKey) ? 10 : 1;
25193           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25194                 increment *= -1;
25195           }
25196           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25197             this.field.dom.value = d.activePage;
25198             return;
25199           }
25200           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25201           {
25202             this.field.dom.value = parseInt(v, 10) + increment;
25203             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25204             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25205           }
25206           e.stopEvent();
25207         }
25208     },
25209
25210     // private
25211     beforeLoad : function(){
25212         if(this.loading){
25213             this.loading.disable();
25214         }
25215     },
25216
25217     // private
25218     onClick : function(which){
25219         
25220         var ds = this.ds;
25221         if (!ds) {
25222             return;
25223         }
25224         
25225         switch(which){
25226             case "first":
25227                 ds.load({params:{start: 0, limit: this.pageSize}});
25228             break;
25229             case "prev":
25230                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25231             break;
25232             case "next":
25233                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25234             break;
25235             case "last":
25236                 var total = ds.getTotalCount();
25237                 var extra = total % this.pageSize;
25238                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25239                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25240             break;
25241             case "refresh":
25242                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25243             break;
25244         }
25245     },
25246
25247     /**
25248      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25249      * @param {Roo.data.Store} store The data store to unbind
25250      */
25251     unbind : function(ds){
25252         ds.un("beforeload", this.beforeLoad, this);
25253         ds.un("load", this.onLoad, this);
25254         ds.un("loadexception", this.onLoadError, this);
25255         ds.un("remove", this.updateInfo, this);
25256         ds.un("add", this.updateInfo, this);
25257         this.ds = undefined;
25258     },
25259
25260     /**
25261      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25262      * @param {Roo.data.Store} store The data store to bind
25263      */
25264     bind : function(ds){
25265         ds.on("beforeload", this.beforeLoad, this);
25266         ds.on("load", this.onLoad, this);
25267         ds.on("loadexception", this.onLoadError, this);
25268         ds.on("remove", this.updateInfo, this);
25269         ds.on("add", this.updateInfo, this);
25270         this.ds = ds;
25271     }
25272 });/*
25273  * - LGPL
25274  *
25275  * element
25276  * 
25277  */
25278
25279 /**
25280  * @class Roo.bootstrap.MessageBar
25281  * @extends Roo.bootstrap.Component
25282  * Bootstrap MessageBar class
25283  * @cfg {String} html contents of the MessageBar
25284  * @cfg {String} weight (info | success | warning | danger) default info
25285  * @cfg {String} beforeClass insert the bar before the given class
25286  * @cfg {Boolean} closable (true | false) default false
25287  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25288  * 
25289  * @constructor
25290  * Create a new Element
25291  * @param {Object} config The config object
25292  */
25293
25294 Roo.bootstrap.MessageBar = function(config){
25295     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25296 };
25297
25298 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25299     
25300     html: '',
25301     weight: 'info',
25302     closable: false,
25303     fixed: false,
25304     beforeClass: 'bootstrap-sticky-wrap',
25305     
25306     getAutoCreate : function(){
25307         
25308         var cfg = {
25309             tag: 'div',
25310             cls: 'alert alert-dismissable alert-' + this.weight,
25311             cn: [
25312                 {
25313                     tag: 'span',
25314                     cls: 'message',
25315                     html: this.html || ''
25316                 }
25317             ]
25318         };
25319         
25320         if(this.fixed){
25321             cfg.cls += ' alert-messages-fixed';
25322         }
25323         
25324         if(this.closable){
25325             cfg.cn.push({
25326                 tag: 'button',
25327                 cls: 'close',
25328                 html: 'x'
25329             });
25330         }
25331         
25332         return cfg;
25333     },
25334     
25335     onRender : function(ct, position)
25336     {
25337         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25338         
25339         if(!this.el){
25340             var cfg = Roo.apply({},  this.getAutoCreate());
25341             cfg.id = Roo.id();
25342             
25343             if (this.cls) {
25344                 cfg.cls += ' ' + this.cls;
25345             }
25346             if (this.style) {
25347                 cfg.style = this.style;
25348             }
25349             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25350             
25351             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25352         }
25353         
25354         this.el.select('>button.close').on('click', this.hide, this);
25355         
25356     },
25357     
25358     show : function()
25359     {
25360         if (!this.rendered) {
25361             this.render();
25362         }
25363         
25364         this.el.show();
25365         
25366         this.fireEvent('show', this);
25367         
25368     },
25369     
25370     hide : function()
25371     {
25372         if (!this.rendered) {
25373             this.render();
25374         }
25375         
25376         this.el.hide();
25377         
25378         this.fireEvent('hide', this);
25379     },
25380     
25381     update : function()
25382     {
25383 //        var e = this.el.dom.firstChild;
25384 //        
25385 //        if(this.closable){
25386 //            e = e.nextSibling;
25387 //        }
25388 //        
25389 //        e.data = this.html || '';
25390
25391         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25392     }
25393    
25394 });
25395
25396  
25397
25398      /*
25399  * - LGPL
25400  *
25401  * Graph
25402  * 
25403  */
25404
25405
25406 /**
25407  * @class Roo.bootstrap.Graph
25408  * @extends Roo.bootstrap.Component
25409  * Bootstrap Graph class
25410 > Prameters
25411  -sm {number} sm 4
25412  -md {number} md 5
25413  @cfg {String} graphtype  bar | vbar | pie
25414  @cfg {number} g_x coodinator | centre x (pie)
25415  @cfg {number} g_y coodinator | centre y (pie)
25416  @cfg {number} g_r radius (pie)
25417  @cfg {number} g_height height of the chart (respected by all elements in the set)
25418  @cfg {number} g_width width of the chart (respected by all elements in the set)
25419  @cfg {Object} title The title of the chart
25420     
25421  -{Array}  values
25422  -opts (object) options for the chart 
25423      o {
25424      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25425      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25426      o vgutter (number)
25427      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.
25428      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25429      o to
25430      o stretch (boolean)
25431      o }
25432  -opts (object) options for the pie
25433      o{
25434      o cut
25435      o startAngle (number)
25436      o endAngle (number)
25437      } 
25438  *
25439  * @constructor
25440  * Create a new Input
25441  * @param {Object} config The config object
25442  */
25443
25444 Roo.bootstrap.Graph = function(config){
25445     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25446     
25447     this.addEvents({
25448         // img events
25449         /**
25450          * @event click
25451          * The img click event for the img.
25452          * @param {Roo.EventObject} e
25453          */
25454         "click" : true
25455     });
25456 };
25457
25458 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25459     
25460     sm: 4,
25461     md: 5,
25462     graphtype: 'bar',
25463     g_height: 250,
25464     g_width: 400,
25465     g_x: 50,
25466     g_y: 50,
25467     g_r: 30,
25468     opts:{
25469         //g_colors: this.colors,
25470         g_type: 'soft',
25471         g_gutter: '20%'
25472
25473     },
25474     title : false,
25475
25476     getAutoCreate : function(){
25477         
25478         var cfg = {
25479             tag: 'div',
25480             html : null
25481         };
25482         
25483         
25484         return  cfg;
25485     },
25486
25487     onRender : function(ct,position){
25488         
25489         
25490         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25491         
25492         if (typeof(Raphael) == 'undefined') {
25493             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25494             return;
25495         }
25496         
25497         this.raphael = Raphael(this.el.dom);
25498         
25499                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25500                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25501                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25502                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25503                 /*
25504                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25505                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25506                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25507                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25508                 
25509                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25510                 r.barchart(330, 10, 300, 220, data1);
25511                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25512                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25513                 */
25514                 
25515                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25516                 // r.barchart(30, 30, 560, 250,  xdata, {
25517                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25518                 //     axis : "0 0 1 1",
25519                 //     axisxlabels :  xdata
25520                 //     //yvalues : cols,
25521                    
25522                 // });
25523 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25524 //        
25525 //        this.load(null,xdata,{
25526 //                axis : "0 0 1 1",
25527 //                axisxlabels :  xdata
25528 //                });
25529
25530     },
25531
25532     load : function(graphtype,xdata,opts)
25533     {
25534         this.raphael.clear();
25535         if(!graphtype) {
25536             graphtype = this.graphtype;
25537         }
25538         if(!opts){
25539             opts = this.opts;
25540         }
25541         var r = this.raphael,
25542             fin = function () {
25543                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25544             },
25545             fout = function () {
25546                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25547             },
25548             pfin = function() {
25549                 this.sector.stop();
25550                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25551
25552                 if (this.label) {
25553                     this.label[0].stop();
25554                     this.label[0].attr({ r: 7.5 });
25555                     this.label[1].attr({ "font-weight": 800 });
25556                 }
25557             },
25558             pfout = function() {
25559                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25560
25561                 if (this.label) {
25562                     this.label[0].animate({ r: 5 }, 500, "bounce");
25563                     this.label[1].attr({ "font-weight": 400 });
25564                 }
25565             };
25566
25567         switch(graphtype){
25568             case 'bar':
25569                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25570                 break;
25571             case 'hbar':
25572                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25573                 break;
25574             case 'pie':
25575 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25576 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25577 //            
25578                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25579                 
25580                 break;
25581
25582         }
25583         
25584         if(this.title){
25585             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25586         }
25587         
25588     },
25589     
25590     setTitle: function(o)
25591     {
25592         this.title = o;
25593     },
25594     
25595     initEvents: function() {
25596         
25597         if(!this.href){
25598             this.el.on('click', this.onClick, this);
25599         }
25600     },
25601     
25602     onClick : function(e)
25603     {
25604         Roo.log('img onclick');
25605         this.fireEvent('click', this, e);
25606     }
25607    
25608 });
25609
25610  
25611 /*
25612  * - LGPL
25613  *
25614  * numberBox
25615  * 
25616  */
25617 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25618
25619 /**
25620  * @class Roo.bootstrap.dash.NumberBox
25621  * @extends Roo.bootstrap.Component
25622  * Bootstrap NumberBox class
25623  * @cfg {String} headline Box headline
25624  * @cfg {String} content Box content
25625  * @cfg {String} icon Box icon
25626  * @cfg {String} footer Footer text
25627  * @cfg {String} fhref Footer href
25628  * 
25629  * @constructor
25630  * Create a new NumberBox
25631  * @param {Object} config The config object
25632  */
25633
25634
25635 Roo.bootstrap.dash.NumberBox = function(config){
25636     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25637     
25638 };
25639
25640 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25641     
25642     headline : '',
25643     content : '',
25644     icon : '',
25645     footer : '',
25646     fhref : '',
25647     ficon : '',
25648     
25649     getAutoCreate : function(){
25650         
25651         var cfg = {
25652             tag : 'div',
25653             cls : 'small-box ',
25654             cn : [
25655                 {
25656                     tag : 'div',
25657                     cls : 'inner',
25658                     cn :[
25659                         {
25660                             tag : 'h3',
25661                             cls : 'roo-headline',
25662                             html : this.headline
25663                         },
25664                         {
25665                             tag : 'p',
25666                             cls : 'roo-content',
25667                             html : this.content
25668                         }
25669                     ]
25670                 }
25671             ]
25672         };
25673         
25674         if(this.icon){
25675             cfg.cn.push({
25676                 tag : 'div',
25677                 cls : 'icon',
25678                 cn :[
25679                     {
25680                         tag : 'i',
25681                         cls : 'ion ' + this.icon
25682                     }
25683                 ]
25684             });
25685         }
25686         
25687         if(this.footer){
25688             var footer = {
25689                 tag : 'a',
25690                 cls : 'small-box-footer',
25691                 href : this.fhref || '#',
25692                 html : this.footer
25693             };
25694             
25695             cfg.cn.push(footer);
25696             
25697         }
25698         
25699         return  cfg;
25700     },
25701
25702     onRender : function(ct,position){
25703         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25704
25705
25706        
25707                 
25708     },
25709
25710     setHeadline: function (value)
25711     {
25712         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25713     },
25714     
25715     setFooter: function (value, href)
25716     {
25717         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25718         
25719         if(href){
25720             this.el.select('a.small-box-footer',true).first().attr('href', href);
25721         }
25722         
25723     },
25724
25725     setContent: function (value)
25726     {
25727         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25728     },
25729
25730     initEvents: function() 
25731     {   
25732         
25733     }
25734     
25735 });
25736
25737  
25738 /*
25739  * - LGPL
25740  *
25741  * TabBox
25742  * 
25743  */
25744 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25745
25746 /**
25747  * @class Roo.bootstrap.dash.TabBox
25748  * @extends Roo.bootstrap.Component
25749  * Bootstrap TabBox class
25750  * @cfg {String} title Title of the TabBox
25751  * @cfg {String} icon Icon of the TabBox
25752  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25753  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25754  * 
25755  * @constructor
25756  * Create a new TabBox
25757  * @param {Object} config The config object
25758  */
25759
25760
25761 Roo.bootstrap.dash.TabBox = function(config){
25762     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25763     this.addEvents({
25764         // raw events
25765         /**
25766          * @event addpane
25767          * When a pane is added
25768          * @param {Roo.bootstrap.dash.TabPane} pane
25769          */
25770         "addpane" : true,
25771         /**
25772          * @event activatepane
25773          * When a pane is activated
25774          * @param {Roo.bootstrap.dash.TabPane} pane
25775          */
25776         "activatepane" : true
25777         
25778          
25779     });
25780     
25781     this.panes = [];
25782 };
25783
25784 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25785
25786     title : '',
25787     icon : false,
25788     showtabs : true,
25789     tabScrollable : false,
25790     
25791     getChildContainer : function()
25792     {
25793         return this.el.select('.tab-content', true).first();
25794     },
25795     
25796     getAutoCreate : function(){
25797         
25798         var header = {
25799             tag: 'li',
25800             cls: 'pull-left header',
25801             html: this.title,
25802             cn : []
25803         };
25804         
25805         if(this.icon){
25806             header.cn.push({
25807                 tag: 'i',
25808                 cls: 'fa ' + this.icon
25809             });
25810         }
25811         
25812         var h = {
25813             tag: 'ul',
25814             cls: 'nav nav-tabs pull-right',
25815             cn: [
25816                 header
25817             ]
25818         };
25819         
25820         if(this.tabScrollable){
25821             h = {
25822                 tag: 'div',
25823                 cls: 'tab-header',
25824                 cn: [
25825                     {
25826                         tag: 'ul',
25827                         cls: 'nav nav-tabs pull-right',
25828                         cn: [
25829                             header
25830                         ]
25831                     }
25832                 ]
25833             };
25834         }
25835         
25836         var cfg = {
25837             tag: 'div',
25838             cls: 'nav-tabs-custom',
25839             cn: [
25840                 h,
25841                 {
25842                     tag: 'div',
25843                     cls: 'tab-content no-padding',
25844                     cn: []
25845                 }
25846             ]
25847         };
25848
25849         return  cfg;
25850     },
25851     initEvents : function()
25852     {
25853         //Roo.log('add add pane handler');
25854         this.on('addpane', this.onAddPane, this);
25855     },
25856      /**
25857      * Updates the box title
25858      * @param {String} html to set the title to.
25859      */
25860     setTitle : function(value)
25861     {
25862         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25863     },
25864     onAddPane : function(pane)
25865     {
25866         this.panes.push(pane);
25867         //Roo.log('addpane');
25868         //Roo.log(pane);
25869         // tabs are rendere left to right..
25870         if(!this.showtabs){
25871             return;
25872         }
25873         
25874         var ctr = this.el.select('.nav-tabs', true).first();
25875          
25876          
25877         var existing = ctr.select('.nav-tab',true);
25878         var qty = existing.getCount();;
25879         
25880         
25881         var tab = ctr.createChild({
25882             tag : 'li',
25883             cls : 'nav-tab' + (qty ? '' : ' active'),
25884             cn : [
25885                 {
25886                     tag : 'a',
25887                     href:'#',
25888                     html : pane.title
25889                 }
25890             ]
25891         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25892         pane.tab = tab;
25893         
25894         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25895         if (!qty) {
25896             pane.el.addClass('active');
25897         }
25898         
25899                 
25900     },
25901     onTabClick : function(ev,un,ob,pane)
25902     {
25903         //Roo.log('tab - prev default');
25904         ev.preventDefault();
25905         
25906         
25907         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25908         pane.tab.addClass('active');
25909         //Roo.log(pane.title);
25910         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25911         // technically we should have a deactivate event.. but maybe add later.
25912         // and it should not de-activate the selected tab...
25913         this.fireEvent('activatepane', pane);
25914         pane.el.addClass('active');
25915         pane.fireEvent('activate');
25916         
25917         
25918     },
25919     
25920     getActivePane : function()
25921     {
25922         var r = false;
25923         Roo.each(this.panes, function(p) {
25924             if(p.el.hasClass('active')){
25925                 r = p;
25926                 return false;
25927             }
25928             
25929             return;
25930         });
25931         
25932         return r;
25933     }
25934     
25935     
25936 });
25937
25938  
25939 /*
25940  * - LGPL
25941  *
25942  * Tab pane
25943  * 
25944  */
25945 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25946 /**
25947  * @class Roo.bootstrap.TabPane
25948  * @extends Roo.bootstrap.Component
25949  * Bootstrap TabPane class
25950  * @cfg {Boolean} active (false | true) Default false
25951  * @cfg {String} title title of panel
25952
25953  * 
25954  * @constructor
25955  * Create a new TabPane
25956  * @param {Object} config The config object
25957  */
25958
25959 Roo.bootstrap.dash.TabPane = function(config){
25960     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25961     
25962     this.addEvents({
25963         // raw events
25964         /**
25965          * @event activate
25966          * When a pane is activated
25967          * @param {Roo.bootstrap.dash.TabPane} pane
25968          */
25969         "activate" : true
25970          
25971     });
25972 };
25973
25974 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25975     
25976     active : false,
25977     title : '',
25978     
25979     // the tabBox that this is attached to.
25980     tab : false,
25981      
25982     getAutoCreate : function() 
25983     {
25984         var cfg = {
25985             tag: 'div',
25986             cls: 'tab-pane'
25987         };
25988         
25989         if(this.active){
25990             cfg.cls += ' active';
25991         }
25992         
25993         return cfg;
25994     },
25995     initEvents  : function()
25996     {
25997         //Roo.log('trigger add pane handler');
25998         this.parent().fireEvent('addpane', this)
25999     },
26000     
26001      /**
26002      * Updates the tab title 
26003      * @param {String} html to set the title to.
26004      */
26005     setTitle: function(str)
26006     {
26007         if (!this.tab) {
26008             return;
26009         }
26010         this.title = str;
26011         this.tab.select('a', true).first().dom.innerHTML = str;
26012         
26013     }
26014     
26015     
26016     
26017 });
26018
26019  
26020
26021
26022  /*
26023  * - LGPL
26024  *
26025  * menu
26026  * 
26027  */
26028 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26029
26030 /**
26031  * @class Roo.bootstrap.menu.Menu
26032  * @extends Roo.bootstrap.Component
26033  * Bootstrap Menu class - container for Menu
26034  * @cfg {String} html Text of the menu
26035  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26036  * @cfg {String} icon Font awesome icon
26037  * @cfg {String} pos Menu align to (top | bottom) default bottom
26038  * 
26039  * 
26040  * @constructor
26041  * Create a new Menu
26042  * @param {Object} config The config object
26043  */
26044
26045
26046 Roo.bootstrap.menu.Menu = function(config){
26047     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26048     
26049     this.addEvents({
26050         /**
26051          * @event beforeshow
26052          * Fires before this menu is displayed
26053          * @param {Roo.bootstrap.menu.Menu} this
26054          */
26055         beforeshow : true,
26056         /**
26057          * @event beforehide
26058          * Fires before this menu is hidden
26059          * @param {Roo.bootstrap.menu.Menu} this
26060          */
26061         beforehide : true,
26062         /**
26063          * @event show
26064          * Fires after this menu is displayed
26065          * @param {Roo.bootstrap.menu.Menu} this
26066          */
26067         show : true,
26068         /**
26069          * @event hide
26070          * Fires after this menu is hidden
26071          * @param {Roo.bootstrap.menu.Menu} this
26072          */
26073         hide : true,
26074         /**
26075          * @event click
26076          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26077          * @param {Roo.bootstrap.menu.Menu} this
26078          * @param {Roo.EventObject} e
26079          */
26080         click : true
26081     });
26082     
26083 };
26084
26085 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26086     
26087     submenu : false,
26088     html : '',
26089     weight : 'default',
26090     icon : false,
26091     pos : 'bottom',
26092     
26093     
26094     getChildContainer : function() {
26095         if(this.isSubMenu){
26096             return this.el;
26097         }
26098         
26099         return this.el.select('ul.dropdown-menu', true).first();  
26100     },
26101     
26102     getAutoCreate : function()
26103     {
26104         var text = [
26105             {
26106                 tag : 'span',
26107                 cls : 'roo-menu-text',
26108                 html : this.html
26109             }
26110         ];
26111         
26112         if(this.icon){
26113             text.unshift({
26114                 tag : 'i',
26115                 cls : 'fa ' + this.icon
26116             })
26117         }
26118         
26119         
26120         var cfg = {
26121             tag : 'div',
26122             cls : 'btn-group',
26123             cn : [
26124                 {
26125                     tag : 'button',
26126                     cls : 'dropdown-button btn btn-' + this.weight,
26127                     cn : text
26128                 },
26129                 {
26130                     tag : 'button',
26131                     cls : 'dropdown-toggle btn btn-' + this.weight,
26132                     cn : [
26133                         {
26134                             tag : 'span',
26135                             cls : 'caret'
26136                         }
26137                     ]
26138                 },
26139                 {
26140                     tag : 'ul',
26141                     cls : 'dropdown-menu'
26142                 }
26143             ]
26144             
26145         };
26146         
26147         if(this.pos == 'top'){
26148             cfg.cls += ' dropup';
26149         }
26150         
26151         if(this.isSubMenu){
26152             cfg = {
26153                 tag : 'ul',
26154                 cls : 'dropdown-menu'
26155             }
26156         }
26157         
26158         return cfg;
26159     },
26160     
26161     onRender : function(ct, position)
26162     {
26163         this.isSubMenu = ct.hasClass('dropdown-submenu');
26164         
26165         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26166     },
26167     
26168     initEvents : function() 
26169     {
26170         if(this.isSubMenu){
26171             return;
26172         }
26173         
26174         this.hidden = true;
26175         
26176         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26177         this.triggerEl.on('click', this.onTriggerPress, this);
26178         
26179         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26180         this.buttonEl.on('click', this.onClick, this);
26181         
26182     },
26183     
26184     list : function()
26185     {
26186         if(this.isSubMenu){
26187             return this.el;
26188         }
26189         
26190         return this.el.select('ul.dropdown-menu', true).first();
26191     },
26192     
26193     onClick : function(e)
26194     {
26195         this.fireEvent("click", this, e);
26196     },
26197     
26198     onTriggerPress  : function(e)
26199     {   
26200         if (this.isVisible()) {
26201             this.hide();
26202         } else {
26203             this.show();
26204         }
26205     },
26206     
26207     isVisible : function(){
26208         return !this.hidden;
26209     },
26210     
26211     show : function()
26212     {
26213         this.fireEvent("beforeshow", this);
26214         
26215         this.hidden = false;
26216         this.el.addClass('open');
26217         
26218         Roo.get(document).on("mouseup", this.onMouseUp, this);
26219         
26220         this.fireEvent("show", this);
26221         
26222         
26223     },
26224     
26225     hide : function()
26226     {
26227         this.fireEvent("beforehide", this);
26228         
26229         this.hidden = true;
26230         this.el.removeClass('open');
26231         
26232         Roo.get(document).un("mouseup", this.onMouseUp);
26233         
26234         this.fireEvent("hide", this);
26235     },
26236     
26237     onMouseUp : function()
26238     {
26239         this.hide();
26240     }
26241     
26242 });
26243
26244  
26245  /*
26246  * - LGPL
26247  *
26248  * menu item
26249  * 
26250  */
26251 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26252
26253 /**
26254  * @class Roo.bootstrap.menu.Item
26255  * @extends Roo.bootstrap.Component
26256  * Bootstrap MenuItem class
26257  * @cfg {Boolean} submenu (true | false) default false
26258  * @cfg {String} html text of the item
26259  * @cfg {String} href the link
26260  * @cfg {Boolean} disable (true | false) default false
26261  * @cfg {Boolean} preventDefault (true | false) default true
26262  * @cfg {String} icon Font awesome icon
26263  * @cfg {String} pos Submenu align to (left | right) default right 
26264  * 
26265  * 
26266  * @constructor
26267  * Create a new Item
26268  * @param {Object} config The config object
26269  */
26270
26271
26272 Roo.bootstrap.menu.Item = function(config){
26273     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26274     this.addEvents({
26275         /**
26276          * @event mouseover
26277          * Fires when the mouse is hovering over this menu
26278          * @param {Roo.bootstrap.menu.Item} this
26279          * @param {Roo.EventObject} e
26280          */
26281         mouseover : true,
26282         /**
26283          * @event mouseout
26284          * Fires when the mouse exits this menu
26285          * @param {Roo.bootstrap.menu.Item} this
26286          * @param {Roo.EventObject} e
26287          */
26288         mouseout : true,
26289         // raw events
26290         /**
26291          * @event click
26292          * The raw click event for the entire grid.
26293          * @param {Roo.EventObject} e
26294          */
26295         click : true
26296     });
26297 };
26298
26299 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26300     
26301     submenu : false,
26302     href : '',
26303     html : '',
26304     preventDefault: true,
26305     disable : false,
26306     icon : false,
26307     pos : 'right',
26308     
26309     getAutoCreate : function()
26310     {
26311         var text = [
26312             {
26313                 tag : 'span',
26314                 cls : 'roo-menu-item-text',
26315                 html : this.html
26316             }
26317         ];
26318         
26319         if(this.icon){
26320             text.unshift({
26321                 tag : 'i',
26322                 cls : 'fa ' + this.icon
26323             })
26324         }
26325         
26326         var cfg = {
26327             tag : 'li',
26328             cn : [
26329                 {
26330                     tag : 'a',
26331                     href : this.href || '#',
26332                     cn : text
26333                 }
26334             ]
26335         };
26336         
26337         if(this.disable){
26338             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26339         }
26340         
26341         if(this.submenu){
26342             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26343             
26344             if(this.pos == 'left'){
26345                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26346             }
26347         }
26348         
26349         return cfg;
26350     },
26351     
26352     initEvents : function() 
26353     {
26354         this.el.on('mouseover', this.onMouseOver, this);
26355         this.el.on('mouseout', this.onMouseOut, this);
26356         
26357         this.el.select('a', true).first().on('click', this.onClick, this);
26358         
26359     },
26360     
26361     onClick : function(e)
26362     {
26363         if(this.preventDefault){
26364             e.preventDefault();
26365         }
26366         
26367         this.fireEvent("click", this, e);
26368     },
26369     
26370     onMouseOver : function(e)
26371     {
26372         if(this.submenu && this.pos == 'left'){
26373             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26374         }
26375         
26376         this.fireEvent("mouseover", this, e);
26377     },
26378     
26379     onMouseOut : function(e)
26380     {
26381         this.fireEvent("mouseout", this, e);
26382     }
26383 });
26384
26385  
26386
26387  /*
26388  * - LGPL
26389  *
26390  * menu separator
26391  * 
26392  */
26393 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26394
26395 /**
26396  * @class Roo.bootstrap.menu.Separator
26397  * @extends Roo.bootstrap.Component
26398  * Bootstrap Separator class
26399  * 
26400  * @constructor
26401  * Create a new Separator
26402  * @param {Object} config The config object
26403  */
26404
26405
26406 Roo.bootstrap.menu.Separator = function(config){
26407     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26408 };
26409
26410 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26411     
26412     getAutoCreate : function(){
26413         var cfg = {
26414             tag : 'li',
26415             cls: 'divider'
26416         };
26417         
26418         return cfg;
26419     }
26420    
26421 });
26422
26423  
26424
26425  /*
26426  * - LGPL
26427  *
26428  * Tooltip
26429  * 
26430  */
26431
26432 /**
26433  * @class Roo.bootstrap.Tooltip
26434  * Bootstrap Tooltip class
26435  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26436  * to determine which dom element triggers the tooltip.
26437  * 
26438  * It needs to add support for additional attributes like tooltip-position
26439  * 
26440  * @constructor
26441  * Create a new Toolti
26442  * @param {Object} config The config object
26443  */
26444
26445 Roo.bootstrap.Tooltip = function(config){
26446     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26447     
26448     this.alignment = Roo.bootstrap.Tooltip.alignment;
26449     
26450     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26451         this.alignment = config.alignment;
26452     }
26453     
26454 };
26455
26456 Roo.apply(Roo.bootstrap.Tooltip, {
26457     /**
26458      * @function init initialize tooltip monitoring.
26459      * @static
26460      */
26461     currentEl : false,
26462     currentTip : false,
26463     currentRegion : false,
26464     
26465     //  init : delay?
26466     
26467     init : function()
26468     {
26469         Roo.get(document).on('mouseover', this.enter ,this);
26470         Roo.get(document).on('mouseout', this.leave, this);
26471          
26472         
26473         this.currentTip = new Roo.bootstrap.Tooltip();
26474     },
26475     
26476     enter : function(ev)
26477     {
26478         var dom = ev.getTarget();
26479         
26480         //Roo.log(['enter',dom]);
26481         var el = Roo.fly(dom);
26482         if (this.currentEl) {
26483             //Roo.log(dom);
26484             //Roo.log(this.currentEl);
26485             //Roo.log(this.currentEl.contains(dom));
26486             if (this.currentEl == el) {
26487                 return;
26488             }
26489             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26490                 return;
26491             }
26492
26493         }
26494         
26495         if (this.currentTip.el) {
26496             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26497         }    
26498         //Roo.log(ev);
26499         
26500         if(!el || el.dom == document){
26501             return;
26502         }
26503         
26504         var bindEl = el;
26505         
26506         // you can not look for children, as if el is the body.. then everythign is the child..
26507         if (!el.attr('tooltip')) { //
26508             if (!el.select("[tooltip]").elements.length) {
26509                 return;
26510             }
26511             // is the mouse over this child...?
26512             bindEl = el.select("[tooltip]").first();
26513             var xy = ev.getXY();
26514             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26515                 //Roo.log("not in region.");
26516                 return;
26517             }
26518             //Roo.log("child element over..");
26519             
26520         }
26521         this.currentEl = bindEl;
26522         this.currentTip.bind(bindEl);
26523         this.currentRegion = Roo.lib.Region.getRegion(dom);
26524         this.currentTip.enter();
26525         
26526     },
26527     leave : function(ev)
26528     {
26529         var dom = ev.getTarget();
26530         //Roo.log(['leave',dom]);
26531         if (!this.currentEl) {
26532             return;
26533         }
26534         
26535         
26536         if (dom != this.currentEl.dom) {
26537             return;
26538         }
26539         var xy = ev.getXY();
26540         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26541             return;
26542         }
26543         // only activate leave if mouse cursor is outside... bounding box..
26544         
26545         
26546         
26547         
26548         if (this.currentTip) {
26549             this.currentTip.leave();
26550         }
26551         //Roo.log('clear currentEl');
26552         this.currentEl = false;
26553         
26554         
26555     },
26556     alignment : {
26557         'left' : ['r-l', [-2,0], 'right'],
26558         'right' : ['l-r', [2,0], 'left'],
26559         'bottom' : ['t-b', [0,2], 'top'],
26560         'top' : [ 'b-t', [0,-2], 'bottom']
26561     }
26562     
26563 });
26564
26565
26566 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26567     
26568     
26569     bindEl : false,
26570     
26571     delay : null, // can be { show : 300 , hide: 500}
26572     
26573     timeout : null,
26574     
26575     hoverState : null, //???
26576     
26577     placement : 'bottom', 
26578     
26579     alignment : false,
26580     
26581     getAutoCreate : function(){
26582     
26583         var cfg = {
26584            cls : 'tooltip',
26585            role : 'tooltip',
26586            cn : [
26587                 {
26588                     cls : 'tooltip-arrow'
26589                 },
26590                 {
26591                     cls : 'tooltip-inner'
26592                 }
26593            ]
26594         };
26595         
26596         return cfg;
26597     },
26598     bind : function(el)
26599     {
26600         this.bindEl = el;
26601     },
26602       
26603     
26604     enter : function () {
26605        
26606         if (this.timeout != null) {
26607             clearTimeout(this.timeout);
26608         }
26609         
26610         this.hoverState = 'in';
26611          //Roo.log("enter - show");
26612         if (!this.delay || !this.delay.show) {
26613             this.show();
26614             return;
26615         }
26616         var _t = this;
26617         this.timeout = setTimeout(function () {
26618             if (_t.hoverState == 'in') {
26619                 _t.show();
26620             }
26621         }, this.delay.show);
26622     },
26623     leave : function()
26624     {
26625         clearTimeout(this.timeout);
26626     
26627         this.hoverState = 'out';
26628          if (!this.delay || !this.delay.hide) {
26629             this.hide();
26630             return;
26631         }
26632        
26633         var _t = this;
26634         this.timeout = setTimeout(function () {
26635             //Roo.log("leave - timeout");
26636             
26637             if (_t.hoverState == 'out') {
26638                 _t.hide();
26639                 Roo.bootstrap.Tooltip.currentEl = false;
26640             }
26641         }, delay);
26642     },
26643     
26644     show : function (msg)
26645     {
26646         if (!this.el) {
26647             this.render(document.body);
26648         }
26649         // set content.
26650         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26651         
26652         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26653         
26654         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26655         
26656         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26657         
26658         var placement = typeof this.placement == 'function' ?
26659             this.placement.call(this, this.el, on_el) :
26660             this.placement;
26661             
26662         var autoToken = /\s?auto?\s?/i;
26663         var autoPlace = autoToken.test(placement);
26664         if (autoPlace) {
26665             placement = placement.replace(autoToken, '') || 'top';
26666         }
26667         
26668         //this.el.detach()
26669         //this.el.setXY([0,0]);
26670         this.el.show();
26671         //this.el.dom.style.display='block';
26672         
26673         //this.el.appendTo(on_el);
26674         
26675         var p = this.getPosition();
26676         var box = this.el.getBox();
26677         
26678         if (autoPlace) {
26679             // fixme..
26680         }
26681         
26682         var align = this.alignment[placement];
26683         
26684         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26685         
26686         if(placement == 'top' || placement == 'bottom'){
26687             if(xy[0] < 0){
26688                 placement = 'right';
26689             }
26690             
26691             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26692                 placement = 'left';
26693             }
26694             
26695             var scroll = Roo.select('body', true).first().getScroll();
26696             
26697             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26698                 placement = 'top';
26699             }
26700             
26701             align = this.alignment[placement];
26702         }
26703         
26704         this.el.alignTo(this.bindEl, align[0],align[1]);
26705         //var arrow = this.el.select('.arrow',true).first();
26706         //arrow.set(align[2], 
26707         
26708         this.el.addClass(placement);
26709         
26710         this.el.addClass('in fade');
26711         
26712         this.hoverState = null;
26713         
26714         if (this.el.hasClass('fade')) {
26715             // fade it?
26716         }
26717         
26718     },
26719     hide : function()
26720     {
26721          
26722         if (!this.el) {
26723             return;
26724         }
26725         //this.el.setXY([0,0]);
26726         this.el.removeClass('in');
26727         //this.el.hide();
26728         
26729     }
26730     
26731 });
26732  
26733
26734  /*
26735  * - LGPL
26736  *
26737  * Location Picker
26738  * 
26739  */
26740
26741 /**
26742  * @class Roo.bootstrap.LocationPicker
26743  * @extends Roo.bootstrap.Component
26744  * Bootstrap LocationPicker class
26745  * @cfg {Number} latitude Position when init default 0
26746  * @cfg {Number} longitude Position when init default 0
26747  * @cfg {Number} zoom default 15
26748  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26749  * @cfg {Boolean} mapTypeControl default false
26750  * @cfg {Boolean} disableDoubleClickZoom default false
26751  * @cfg {Boolean} scrollwheel default true
26752  * @cfg {Boolean} streetViewControl default false
26753  * @cfg {Number} radius default 0
26754  * @cfg {String} locationName
26755  * @cfg {Boolean} draggable default true
26756  * @cfg {Boolean} enableAutocomplete default false
26757  * @cfg {Boolean} enableReverseGeocode default true
26758  * @cfg {String} markerTitle
26759  * 
26760  * @constructor
26761  * Create a new LocationPicker
26762  * @param {Object} config The config object
26763  */
26764
26765
26766 Roo.bootstrap.LocationPicker = function(config){
26767     
26768     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26769     
26770     this.addEvents({
26771         /**
26772          * @event initial
26773          * Fires when the picker initialized.
26774          * @param {Roo.bootstrap.LocationPicker} this
26775          * @param {Google Location} location
26776          */
26777         initial : true,
26778         /**
26779          * @event positionchanged
26780          * Fires when the picker position changed.
26781          * @param {Roo.bootstrap.LocationPicker} this
26782          * @param {Google Location} location
26783          */
26784         positionchanged : true,
26785         /**
26786          * @event resize
26787          * Fires when the map resize.
26788          * @param {Roo.bootstrap.LocationPicker} this
26789          */
26790         resize : true,
26791         /**
26792          * @event show
26793          * Fires when the map show.
26794          * @param {Roo.bootstrap.LocationPicker} this
26795          */
26796         show : true,
26797         /**
26798          * @event hide
26799          * Fires when the map hide.
26800          * @param {Roo.bootstrap.LocationPicker} this
26801          */
26802         hide : true,
26803         /**
26804          * @event mapClick
26805          * Fires when click the map.
26806          * @param {Roo.bootstrap.LocationPicker} this
26807          * @param {Map event} e
26808          */
26809         mapClick : true,
26810         /**
26811          * @event mapRightClick
26812          * Fires when right click the map.
26813          * @param {Roo.bootstrap.LocationPicker} this
26814          * @param {Map event} e
26815          */
26816         mapRightClick : true,
26817         /**
26818          * @event markerClick
26819          * Fires when click the marker.
26820          * @param {Roo.bootstrap.LocationPicker} this
26821          * @param {Map event} e
26822          */
26823         markerClick : true,
26824         /**
26825          * @event markerRightClick
26826          * Fires when right click the marker.
26827          * @param {Roo.bootstrap.LocationPicker} this
26828          * @param {Map event} e
26829          */
26830         markerRightClick : true,
26831         /**
26832          * @event OverlayViewDraw
26833          * Fires when OverlayView Draw
26834          * @param {Roo.bootstrap.LocationPicker} this
26835          */
26836         OverlayViewDraw : true,
26837         /**
26838          * @event OverlayViewOnAdd
26839          * Fires when OverlayView Draw
26840          * @param {Roo.bootstrap.LocationPicker} this
26841          */
26842         OverlayViewOnAdd : true,
26843         /**
26844          * @event OverlayViewOnRemove
26845          * Fires when OverlayView Draw
26846          * @param {Roo.bootstrap.LocationPicker} this
26847          */
26848         OverlayViewOnRemove : true,
26849         /**
26850          * @event OverlayViewShow
26851          * Fires when OverlayView Draw
26852          * @param {Roo.bootstrap.LocationPicker} this
26853          * @param {Pixel} cpx
26854          */
26855         OverlayViewShow : true,
26856         /**
26857          * @event OverlayViewHide
26858          * Fires when OverlayView Draw
26859          * @param {Roo.bootstrap.LocationPicker} this
26860          */
26861         OverlayViewHide : true,
26862         /**
26863          * @event loadexception
26864          * Fires when load google lib failed.
26865          * @param {Roo.bootstrap.LocationPicker} this
26866          */
26867         loadexception : true
26868     });
26869         
26870 };
26871
26872 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26873     
26874     gMapContext: false,
26875     
26876     latitude: 0,
26877     longitude: 0,
26878     zoom: 15,
26879     mapTypeId: false,
26880     mapTypeControl: false,
26881     disableDoubleClickZoom: false,
26882     scrollwheel: true,
26883     streetViewControl: false,
26884     radius: 0,
26885     locationName: '',
26886     draggable: true,
26887     enableAutocomplete: false,
26888     enableReverseGeocode: true,
26889     markerTitle: '',
26890     
26891     getAutoCreate: function()
26892     {
26893
26894         var cfg = {
26895             tag: 'div',
26896             cls: 'roo-location-picker'
26897         };
26898         
26899         return cfg
26900     },
26901     
26902     initEvents: function(ct, position)
26903     {       
26904         if(!this.el.getWidth() || this.isApplied()){
26905             return;
26906         }
26907         
26908         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26909         
26910         this.initial();
26911     },
26912     
26913     initial: function()
26914     {
26915         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26916             this.fireEvent('loadexception', this);
26917             return;
26918         }
26919         
26920         if(!this.mapTypeId){
26921             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26922         }
26923         
26924         this.gMapContext = this.GMapContext();
26925         
26926         this.initOverlayView();
26927         
26928         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26929         
26930         var _this = this;
26931                 
26932         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26933             _this.setPosition(_this.gMapContext.marker.position);
26934         });
26935         
26936         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26937             _this.fireEvent('mapClick', this, event);
26938             
26939         });
26940
26941         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26942             _this.fireEvent('mapRightClick', this, event);
26943             
26944         });
26945         
26946         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26947             _this.fireEvent('markerClick', this, event);
26948             
26949         });
26950
26951         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26952             _this.fireEvent('markerRightClick', this, event);
26953             
26954         });
26955         
26956         this.setPosition(this.gMapContext.location);
26957         
26958         this.fireEvent('initial', this, this.gMapContext.location);
26959     },
26960     
26961     initOverlayView: function()
26962     {
26963         var _this = this;
26964         
26965         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26966             
26967             draw: function()
26968             {
26969                 _this.fireEvent('OverlayViewDraw', _this);
26970             },
26971             
26972             onAdd: function()
26973             {
26974                 _this.fireEvent('OverlayViewOnAdd', _this);
26975             },
26976             
26977             onRemove: function()
26978             {
26979                 _this.fireEvent('OverlayViewOnRemove', _this);
26980             },
26981             
26982             show: function(cpx)
26983             {
26984                 _this.fireEvent('OverlayViewShow', _this, cpx);
26985             },
26986             
26987             hide: function()
26988             {
26989                 _this.fireEvent('OverlayViewHide', _this);
26990             }
26991             
26992         });
26993     },
26994     
26995     fromLatLngToContainerPixel: function(event)
26996     {
26997         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26998     },
26999     
27000     isApplied: function() 
27001     {
27002         return this.getGmapContext() == false ? false : true;
27003     },
27004     
27005     getGmapContext: function() 
27006     {
27007         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27008     },
27009     
27010     GMapContext: function() 
27011     {
27012         var position = new google.maps.LatLng(this.latitude, this.longitude);
27013         
27014         var _map = new google.maps.Map(this.el.dom, {
27015             center: position,
27016             zoom: this.zoom,
27017             mapTypeId: this.mapTypeId,
27018             mapTypeControl: this.mapTypeControl,
27019             disableDoubleClickZoom: this.disableDoubleClickZoom,
27020             scrollwheel: this.scrollwheel,
27021             streetViewControl: this.streetViewControl,
27022             locationName: this.locationName,
27023             draggable: this.draggable,
27024             enableAutocomplete: this.enableAutocomplete,
27025             enableReverseGeocode: this.enableReverseGeocode
27026         });
27027         
27028         var _marker = new google.maps.Marker({
27029             position: position,
27030             map: _map,
27031             title: this.markerTitle,
27032             draggable: this.draggable
27033         });
27034         
27035         return {
27036             map: _map,
27037             marker: _marker,
27038             circle: null,
27039             location: position,
27040             radius: this.radius,
27041             locationName: this.locationName,
27042             addressComponents: {
27043                 formatted_address: null,
27044                 addressLine1: null,
27045                 addressLine2: null,
27046                 streetName: null,
27047                 streetNumber: null,
27048                 city: null,
27049                 district: null,
27050                 state: null,
27051                 stateOrProvince: null
27052             },
27053             settings: this,
27054             domContainer: this.el.dom,
27055             geodecoder: new google.maps.Geocoder()
27056         };
27057     },
27058     
27059     drawCircle: function(center, radius, options) 
27060     {
27061         if (this.gMapContext.circle != null) {
27062             this.gMapContext.circle.setMap(null);
27063         }
27064         if (radius > 0) {
27065             radius *= 1;
27066             options = Roo.apply({}, options, {
27067                 strokeColor: "#0000FF",
27068                 strokeOpacity: .35,
27069                 strokeWeight: 2,
27070                 fillColor: "#0000FF",
27071                 fillOpacity: .2
27072             });
27073             
27074             options.map = this.gMapContext.map;
27075             options.radius = radius;
27076             options.center = center;
27077             this.gMapContext.circle = new google.maps.Circle(options);
27078             return this.gMapContext.circle;
27079         }
27080         
27081         return null;
27082     },
27083     
27084     setPosition: function(location) 
27085     {
27086         this.gMapContext.location = location;
27087         this.gMapContext.marker.setPosition(location);
27088         this.gMapContext.map.panTo(location);
27089         this.drawCircle(location, this.gMapContext.radius, {});
27090         
27091         var _this = this;
27092         
27093         if (this.gMapContext.settings.enableReverseGeocode) {
27094             this.gMapContext.geodecoder.geocode({
27095                 latLng: this.gMapContext.location
27096             }, function(results, status) {
27097                 
27098                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27099                     _this.gMapContext.locationName = results[0].formatted_address;
27100                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27101                     
27102                     _this.fireEvent('positionchanged', this, location);
27103                 }
27104             });
27105             
27106             return;
27107         }
27108         
27109         this.fireEvent('positionchanged', this, location);
27110     },
27111     
27112     resize: function()
27113     {
27114         google.maps.event.trigger(this.gMapContext.map, "resize");
27115         
27116         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27117         
27118         this.fireEvent('resize', this);
27119     },
27120     
27121     setPositionByLatLng: function(latitude, longitude)
27122     {
27123         this.setPosition(new google.maps.LatLng(latitude, longitude));
27124     },
27125     
27126     getCurrentPosition: function() 
27127     {
27128         return {
27129             latitude: this.gMapContext.location.lat(),
27130             longitude: this.gMapContext.location.lng()
27131         };
27132     },
27133     
27134     getAddressName: function() 
27135     {
27136         return this.gMapContext.locationName;
27137     },
27138     
27139     getAddressComponents: function() 
27140     {
27141         return this.gMapContext.addressComponents;
27142     },
27143     
27144     address_component_from_google_geocode: function(address_components) 
27145     {
27146         var result = {};
27147         
27148         for (var i = 0; i < address_components.length; i++) {
27149             var component = address_components[i];
27150             if (component.types.indexOf("postal_code") >= 0) {
27151                 result.postalCode = component.short_name;
27152             } else if (component.types.indexOf("street_number") >= 0) {
27153                 result.streetNumber = component.short_name;
27154             } else if (component.types.indexOf("route") >= 0) {
27155                 result.streetName = component.short_name;
27156             } else if (component.types.indexOf("neighborhood") >= 0) {
27157                 result.city = component.short_name;
27158             } else if (component.types.indexOf("locality") >= 0) {
27159                 result.city = component.short_name;
27160             } else if (component.types.indexOf("sublocality") >= 0) {
27161                 result.district = component.short_name;
27162             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27163                 result.stateOrProvince = component.short_name;
27164             } else if (component.types.indexOf("country") >= 0) {
27165                 result.country = component.short_name;
27166             }
27167         }
27168         
27169         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27170         result.addressLine2 = "";
27171         return result;
27172     },
27173     
27174     setZoomLevel: function(zoom)
27175     {
27176         this.gMapContext.map.setZoom(zoom);
27177     },
27178     
27179     show: function()
27180     {
27181         if(!this.el){
27182             return;
27183         }
27184         
27185         this.el.show();
27186         
27187         this.resize();
27188         
27189         this.fireEvent('show', this);
27190     },
27191     
27192     hide: function()
27193     {
27194         if(!this.el){
27195             return;
27196         }
27197         
27198         this.el.hide();
27199         
27200         this.fireEvent('hide', this);
27201     }
27202     
27203 });
27204
27205 Roo.apply(Roo.bootstrap.LocationPicker, {
27206     
27207     OverlayView : function(map, options)
27208     {
27209         options = options || {};
27210         
27211         this.setMap(map);
27212     }
27213     
27214     
27215 });/**
27216  * @class Roo.bootstrap.Alert
27217  * @extends Roo.bootstrap.Component
27218  * Bootstrap Alert class - shows an alert area box
27219  * eg
27220  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27221   Enter a valid email address
27222 </div>
27223  * @licence LGPL
27224  * @cfg {String} title The title of alert
27225  * @cfg {String} html The content of alert
27226  * @cfg {String} weight (  success | info | warning | danger )
27227  * @cfg {String} faicon font-awesomeicon
27228  * 
27229  * @constructor
27230  * Create a new alert
27231  * @param {Object} config The config object
27232  */
27233
27234
27235 Roo.bootstrap.Alert = function(config){
27236     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27237     
27238 };
27239
27240 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27241     
27242     title: '',
27243     html: '',
27244     weight: false,
27245     faicon: false,
27246     
27247     getAutoCreate : function()
27248     {
27249         
27250         var cfg = {
27251             tag : 'div',
27252             cls : 'alert',
27253             cn : [
27254                 {
27255                     tag : 'i',
27256                     cls : 'roo-alert-icon'
27257                     
27258                 },
27259                 {
27260                     tag : 'b',
27261                     cls : 'roo-alert-title',
27262                     html : this.title
27263                 },
27264                 {
27265                     tag : 'span',
27266                     cls : 'roo-alert-text',
27267                     html : this.html
27268                 }
27269             ]
27270         };
27271         
27272         if(this.faicon){
27273             cfg.cn[0].cls += ' fa ' + this.faicon;
27274         }
27275         
27276         if(this.weight){
27277             cfg.cls += ' alert-' + this.weight;
27278         }
27279         
27280         return cfg;
27281     },
27282     
27283     initEvents: function() 
27284     {
27285         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27286     },
27287     
27288     setTitle : function(str)
27289     {
27290         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27291     },
27292     
27293     setText : function(str)
27294     {
27295         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27296     },
27297     
27298     setWeight : function(weight)
27299     {
27300         if(this.weight){
27301             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27302         }
27303         
27304         this.weight = weight;
27305         
27306         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27307     },
27308     
27309     setIcon : function(icon)
27310     {
27311         if(this.faicon){
27312             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27313         }
27314         
27315         this.faicon = icon;
27316         
27317         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27318     },
27319     
27320     hide: function() 
27321     {
27322         this.el.hide();   
27323     },
27324     
27325     show: function() 
27326     {  
27327         this.el.show();   
27328     }
27329     
27330 });
27331
27332  
27333 /*
27334 * Licence: LGPL
27335 */
27336
27337 /**
27338  * @class Roo.bootstrap.UploadCropbox
27339  * @extends Roo.bootstrap.Component
27340  * Bootstrap UploadCropbox class
27341  * @cfg {String} emptyText show when image has been loaded
27342  * @cfg {String} rotateNotify show when image too small to rotate
27343  * @cfg {Number} errorTimeout default 3000
27344  * @cfg {Number} minWidth default 300
27345  * @cfg {Number} minHeight default 300
27346  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27347  * @cfg {Boolean} isDocument (true|false) default false
27348  * @cfg {String} url action url
27349  * @cfg {String} paramName default 'imageUpload'
27350  * @cfg {String} method default POST
27351  * @cfg {Boolean} loadMask (true|false) default true
27352  * @cfg {Boolean} loadingText default 'Loading...'
27353  * 
27354  * @constructor
27355  * Create a new UploadCropbox
27356  * @param {Object} config The config object
27357  */
27358
27359 Roo.bootstrap.UploadCropbox = function(config){
27360     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27361     
27362     this.addEvents({
27363         /**
27364          * @event beforeselectfile
27365          * Fire before select file
27366          * @param {Roo.bootstrap.UploadCropbox} this
27367          */
27368         "beforeselectfile" : true,
27369         /**
27370          * @event initial
27371          * Fire after initEvent
27372          * @param {Roo.bootstrap.UploadCropbox} this
27373          */
27374         "initial" : true,
27375         /**
27376          * @event crop
27377          * Fire after initEvent
27378          * @param {Roo.bootstrap.UploadCropbox} this
27379          * @param {String} data
27380          */
27381         "crop" : true,
27382         /**
27383          * @event prepare
27384          * Fire when preparing the file data
27385          * @param {Roo.bootstrap.UploadCropbox} this
27386          * @param {Object} file
27387          */
27388         "prepare" : true,
27389         /**
27390          * @event exception
27391          * Fire when get exception
27392          * @param {Roo.bootstrap.UploadCropbox} this
27393          * @param {XMLHttpRequest} xhr
27394          */
27395         "exception" : true,
27396         /**
27397          * @event beforeloadcanvas
27398          * Fire before load the canvas
27399          * @param {Roo.bootstrap.UploadCropbox} this
27400          * @param {String} src
27401          */
27402         "beforeloadcanvas" : true,
27403         /**
27404          * @event trash
27405          * Fire when trash image
27406          * @param {Roo.bootstrap.UploadCropbox} this
27407          */
27408         "trash" : true,
27409         /**
27410          * @event download
27411          * Fire when download the image
27412          * @param {Roo.bootstrap.UploadCropbox} this
27413          */
27414         "download" : true,
27415         /**
27416          * @event footerbuttonclick
27417          * Fire when footerbuttonclick
27418          * @param {Roo.bootstrap.UploadCropbox} this
27419          * @param {String} type
27420          */
27421         "footerbuttonclick" : true,
27422         /**
27423          * @event resize
27424          * Fire when resize
27425          * @param {Roo.bootstrap.UploadCropbox} this
27426          */
27427         "resize" : true,
27428         /**
27429          * @event rotate
27430          * Fire when rotate the image
27431          * @param {Roo.bootstrap.UploadCropbox} this
27432          * @param {String} pos
27433          */
27434         "rotate" : true,
27435         /**
27436          * @event inspect
27437          * Fire when inspect the file
27438          * @param {Roo.bootstrap.UploadCropbox} this
27439          * @param {Object} file
27440          */
27441         "inspect" : true,
27442         /**
27443          * @event upload
27444          * Fire when xhr upload the file
27445          * @param {Roo.bootstrap.UploadCropbox} this
27446          * @param {Object} data
27447          */
27448         "upload" : true,
27449         /**
27450          * @event arrange
27451          * Fire when arrange the file data
27452          * @param {Roo.bootstrap.UploadCropbox} this
27453          * @param {Object} formData
27454          */
27455         "arrange" : true
27456     });
27457     
27458     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27459 };
27460
27461 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27462     
27463     emptyText : 'Click to upload image',
27464     rotateNotify : 'Image is too small to rotate',
27465     errorTimeout : 3000,
27466     scale : 0,
27467     baseScale : 1,
27468     rotate : 0,
27469     dragable : false,
27470     pinching : false,
27471     mouseX : 0,
27472     mouseY : 0,
27473     cropData : false,
27474     minWidth : 300,
27475     minHeight : 300,
27476     file : false,
27477     exif : {},
27478     baseRotate : 1,
27479     cropType : 'image/jpeg',
27480     buttons : false,
27481     canvasLoaded : false,
27482     isDocument : false,
27483     method : 'POST',
27484     paramName : 'imageUpload',
27485     loadMask : true,
27486     loadingText : 'Loading...',
27487     maskEl : false,
27488     
27489     getAutoCreate : function()
27490     {
27491         var cfg = {
27492             tag : 'div',
27493             cls : 'roo-upload-cropbox',
27494             cn : [
27495                 {
27496                     tag : 'input',
27497                     cls : 'roo-upload-cropbox-selector',
27498                     type : 'file'
27499                 },
27500                 {
27501                     tag : 'div',
27502                     cls : 'roo-upload-cropbox-body',
27503                     style : 'cursor:pointer',
27504                     cn : [
27505                         {
27506                             tag : 'div',
27507                             cls : 'roo-upload-cropbox-preview'
27508                         },
27509                         {
27510                             tag : 'div',
27511                             cls : 'roo-upload-cropbox-thumb'
27512                         },
27513                         {
27514                             tag : 'div',
27515                             cls : 'roo-upload-cropbox-empty-notify',
27516                             html : this.emptyText
27517                         },
27518                         {
27519                             tag : 'div',
27520                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27521                             html : this.rotateNotify
27522                         }
27523                     ]
27524                 },
27525                 {
27526                     tag : 'div',
27527                     cls : 'roo-upload-cropbox-footer',
27528                     cn : {
27529                         tag : 'div',
27530                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27531                         cn : []
27532                     }
27533                 }
27534             ]
27535         };
27536         
27537         return cfg;
27538     },
27539     
27540     onRender : function(ct, position)
27541     {
27542         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27543         
27544         if (this.buttons.length) {
27545             
27546             Roo.each(this.buttons, function(bb) {
27547                 
27548                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27549                 
27550                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27551                 
27552             }, this);
27553         }
27554         
27555         if(this.loadMask){
27556             this.maskEl = this.el;
27557         }
27558     },
27559     
27560     initEvents : function()
27561     {
27562         this.urlAPI = (window.createObjectURL && window) || 
27563                                 (window.URL && URL.revokeObjectURL && URL) || 
27564                                 (window.webkitURL && webkitURL);
27565                         
27566         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27567         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27568         
27569         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27570         this.selectorEl.hide();
27571         
27572         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27573         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27574         
27575         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27576         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27577         this.thumbEl.hide();
27578         
27579         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27580         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27581         
27582         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27583         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27584         this.errorEl.hide();
27585         
27586         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27587         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27588         this.footerEl.hide();
27589         
27590         this.setThumbBoxSize();
27591         
27592         this.bind();
27593         
27594         this.resize();
27595         
27596         this.fireEvent('initial', this);
27597     },
27598
27599     bind : function()
27600     {
27601         var _this = this;
27602         
27603         window.addEventListener("resize", function() { _this.resize(); } );
27604         
27605         this.bodyEl.on('click', this.beforeSelectFile, this);
27606         
27607         if(Roo.isTouch){
27608             this.bodyEl.on('touchstart', this.onTouchStart, this);
27609             this.bodyEl.on('touchmove', this.onTouchMove, this);
27610             this.bodyEl.on('touchend', this.onTouchEnd, this);
27611         }
27612         
27613         if(!Roo.isTouch){
27614             this.bodyEl.on('mousedown', this.onMouseDown, this);
27615             this.bodyEl.on('mousemove', this.onMouseMove, this);
27616             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27617             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27618             Roo.get(document).on('mouseup', this.onMouseUp, this);
27619         }
27620         
27621         this.selectorEl.on('change', this.onFileSelected, this);
27622     },
27623     
27624     reset : function()
27625     {    
27626         this.scale = 0;
27627         this.baseScale = 1;
27628         this.rotate = 0;
27629         this.baseRotate = 1;
27630         this.dragable = false;
27631         this.pinching = false;
27632         this.mouseX = 0;
27633         this.mouseY = 0;
27634         this.cropData = false;
27635         this.notifyEl.dom.innerHTML = this.emptyText;
27636         
27637         this.selectorEl.dom.value = '';
27638         
27639     },
27640     
27641     resize : function()
27642     {
27643         if(this.fireEvent('resize', this) != false){
27644             this.setThumbBoxPosition();
27645             this.setCanvasPosition();
27646         }
27647     },
27648     
27649     onFooterButtonClick : function(e, el, o, type)
27650     {
27651         switch (type) {
27652             case 'rotate-left' :
27653                 this.onRotateLeft(e);
27654                 break;
27655             case 'rotate-right' :
27656                 this.onRotateRight(e);
27657                 break;
27658             case 'picture' :
27659                 this.beforeSelectFile(e);
27660                 break;
27661             case 'trash' :
27662                 this.trash(e);
27663                 break;
27664             case 'crop' :
27665                 this.crop(e);
27666                 break;
27667             case 'download' :
27668                 this.download(e);
27669                 break;
27670             default :
27671                 break;
27672         }
27673         
27674         this.fireEvent('footerbuttonclick', this, type);
27675     },
27676     
27677     beforeSelectFile : function(e)
27678     {
27679         e.preventDefault();
27680         
27681         if(this.fireEvent('beforeselectfile', this) != false){
27682             this.selectorEl.dom.click();
27683         }
27684     },
27685     
27686     onFileSelected : function(e)
27687     {
27688         e.preventDefault();
27689         
27690         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27691             return;
27692         }
27693         
27694         var file = this.selectorEl.dom.files[0];
27695         
27696         if(this.fireEvent('inspect', this, file) != false){
27697             this.prepare(file);
27698         }
27699         
27700     },
27701     
27702     trash : function(e)
27703     {
27704         this.fireEvent('trash', this);
27705     },
27706     
27707     download : function(e)
27708     {
27709         this.fireEvent('download', this);
27710     },
27711     
27712     loadCanvas : function(src)
27713     {   
27714         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27715             
27716             this.reset();
27717             
27718             this.imageEl = document.createElement('img');
27719             
27720             var _this = this;
27721             
27722             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27723             
27724             this.imageEl.src = src;
27725         }
27726     },
27727     
27728     onLoadCanvas : function()
27729     {   
27730         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27731         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27732         
27733         this.bodyEl.un('click', this.beforeSelectFile, this);
27734         
27735         this.notifyEl.hide();
27736         this.thumbEl.show();
27737         this.footerEl.show();
27738         
27739         this.baseRotateLevel();
27740         
27741         if(this.isDocument){
27742             this.setThumbBoxSize();
27743         }
27744         
27745         this.setThumbBoxPosition();
27746         
27747         this.baseScaleLevel();
27748         
27749         this.draw();
27750         
27751         this.resize();
27752         
27753         this.canvasLoaded = true;
27754         
27755         if(this.loadMask){
27756             this.maskEl.unmask();
27757         }
27758         
27759     },
27760     
27761     setCanvasPosition : function()
27762     {   
27763         if(!this.canvasEl){
27764             return;
27765         }
27766         
27767         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27768         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27769         
27770         this.previewEl.setLeft(pw);
27771         this.previewEl.setTop(ph);
27772         
27773     },
27774     
27775     onMouseDown : function(e)
27776     {   
27777         e.stopEvent();
27778         
27779         this.dragable = true;
27780         this.pinching = false;
27781         
27782         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27783             this.dragable = false;
27784             return;
27785         }
27786         
27787         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27788         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27789         
27790     },
27791     
27792     onMouseMove : function(e)
27793     {   
27794         e.stopEvent();
27795         
27796         if(!this.canvasLoaded){
27797             return;
27798         }
27799         
27800         if (!this.dragable){
27801             return;
27802         }
27803         
27804         var minX = Math.ceil(this.thumbEl.getLeft(true));
27805         var minY = Math.ceil(this.thumbEl.getTop(true));
27806         
27807         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27808         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27809         
27810         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27811         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27812         
27813         x = x - this.mouseX;
27814         y = y - this.mouseY;
27815         
27816         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27817         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27818         
27819         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27820         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27821         
27822         this.previewEl.setLeft(bgX);
27823         this.previewEl.setTop(bgY);
27824         
27825         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27826         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27827     },
27828     
27829     onMouseUp : function(e)
27830     {   
27831         e.stopEvent();
27832         
27833         this.dragable = false;
27834     },
27835     
27836     onMouseWheel : function(e)
27837     {   
27838         e.stopEvent();
27839         
27840         this.startScale = this.scale;
27841         
27842         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27843         
27844         if(!this.zoomable()){
27845             this.scale = this.startScale;
27846             return;
27847         }
27848         
27849         this.draw();
27850         
27851         return;
27852     },
27853     
27854     zoomable : function()
27855     {
27856         var minScale = this.thumbEl.getWidth() / this.minWidth;
27857         
27858         if(this.minWidth < this.minHeight){
27859             minScale = this.thumbEl.getHeight() / this.minHeight;
27860         }
27861         
27862         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27863         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27864         
27865         if(
27866                 this.isDocument &&
27867                 (this.rotate == 0 || this.rotate == 180) && 
27868                 (
27869                     width > this.imageEl.OriginWidth || 
27870                     height > this.imageEl.OriginHeight ||
27871                     (width < this.minWidth && height < this.minHeight)
27872                 )
27873         ){
27874             return false;
27875         }
27876         
27877         if(
27878                 this.isDocument &&
27879                 (this.rotate == 90 || this.rotate == 270) && 
27880                 (
27881                     width > this.imageEl.OriginWidth || 
27882                     height > this.imageEl.OriginHeight ||
27883                     (width < this.minHeight && height < this.minWidth)
27884                 )
27885         ){
27886             return false;
27887         }
27888         
27889         if(
27890                 !this.isDocument &&
27891                 (this.rotate == 0 || this.rotate == 180) && 
27892                 (
27893                     width < this.minWidth || 
27894                     width > this.imageEl.OriginWidth || 
27895                     height < this.minHeight || 
27896                     height > this.imageEl.OriginHeight
27897                 )
27898         ){
27899             return false;
27900         }
27901         
27902         if(
27903                 !this.isDocument &&
27904                 (this.rotate == 90 || this.rotate == 270) && 
27905                 (
27906                     width < this.minHeight || 
27907                     width > this.imageEl.OriginWidth || 
27908                     height < this.minWidth || 
27909                     height > this.imageEl.OriginHeight
27910                 )
27911         ){
27912             return false;
27913         }
27914         
27915         return true;
27916         
27917     },
27918     
27919     onRotateLeft : function(e)
27920     {   
27921         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27922             
27923             var minScale = this.thumbEl.getWidth() / this.minWidth;
27924             
27925             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27926             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27927             
27928             this.startScale = this.scale;
27929             
27930             while (this.getScaleLevel() < minScale){
27931             
27932                 this.scale = this.scale + 1;
27933                 
27934                 if(!this.zoomable()){
27935                     break;
27936                 }
27937                 
27938                 if(
27939                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27940                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27941                 ){
27942                     continue;
27943                 }
27944                 
27945                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27946
27947                 this.draw();
27948                 
27949                 return;
27950             }
27951             
27952             this.scale = this.startScale;
27953             
27954             this.onRotateFail();
27955             
27956             return false;
27957         }
27958         
27959         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27960
27961         if(this.isDocument){
27962             this.setThumbBoxSize();
27963             this.setThumbBoxPosition();
27964             this.setCanvasPosition();
27965         }
27966         
27967         this.draw();
27968         
27969         this.fireEvent('rotate', this, 'left');
27970         
27971     },
27972     
27973     onRotateRight : function(e)
27974     {
27975         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27976             
27977             var minScale = this.thumbEl.getWidth() / this.minWidth;
27978         
27979             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27980             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27981             
27982             this.startScale = this.scale;
27983             
27984             while (this.getScaleLevel() < minScale){
27985             
27986                 this.scale = this.scale + 1;
27987                 
27988                 if(!this.zoomable()){
27989                     break;
27990                 }
27991                 
27992                 if(
27993                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27994                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27995                 ){
27996                     continue;
27997                 }
27998                 
27999                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28000
28001                 this.draw();
28002                 
28003                 return;
28004             }
28005             
28006             this.scale = this.startScale;
28007             
28008             this.onRotateFail();
28009             
28010             return false;
28011         }
28012         
28013         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28014
28015         if(this.isDocument){
28016             this.setThumbBoxSize();
28017             this.setThumbBoxPosition();
28018             this.setCanvasPosition();
28019         }
28020         
28021         this.draw();
28022         
28023         this.fireEvent('rotate', this, 'right');
28024     },
28025     
28026     onRotateFail : function()
28027     {
28028         this.errorEl.show(true);
28029         
28030         var _this = this;
28031         
28032         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28033     },
28034     
28035     draw : function()
28036     {
28037         this.previewEl.dom.innerHTML = '';
28038         
28039         var canvasEl = document.createElement("canvas");
28040         
28041         var contextEl = canvasEl.getContext("2d");
28042         
28043         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28044         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28045         var center = this.imageEl.OriginWidth / 2;
28046         
28047         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28048             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28049             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28050             center = this.imageEl.OriginHeight / 2;
28051         }
28052         
28053         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28054         
28055         contextEl.translate(center, center);
28056         contextEl.rotate(this.rotate * Math.PI / 180);
28057
28058         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28059         
28060         this.canvasEl = document.createElement("canvas");
28061         
28062         this.contextEl = this.canvasEl.getContext("2d");
28063         
28064         switch (this.rotate) {
28065             case 0 :
28066                 
28067                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28068                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28069                 
28070                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28071                 
28072                 break;
28073             case 90 : 
28074                 
28075                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28076                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28077                 
28078                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28079                     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);
28080                     break;
28081                 }
28082                 
28083                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28084                 
28085                 break;
28086             case 180 :
28087                 
28088                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28089                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28090                 
28091                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28092                     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);
28093                     break;
28094                 }
28095                 
28096                 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);
28097                 
28098                 break;
28099             case 270 :
28100                 
28101                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28102                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28103         
28104                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28105                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28106                     break;
28107                 }
28108                 
28109                 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);
28110                 
28111                 break;
28112             default : 
28113                 break;
28114         }
28115         
28116         this.previewEl.appendChild(this.canvasEl);
28117         
28118         this.setCanvasPosition();
28119     },
28120     
28121     crop : function()
28122     {
28123         if(!this.canvasLoaded){
28124             return;
28125         }
28126         
28127         var imageCanvas = document.createElement("canvas");
28128         
28129         var imageContext = imageCanvas.getContext("2d");
28130         
28131         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28132         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28133         
28134         var center = imageCanvas.width / 2;
28135         
28136         imageContext.translate(center, center);
28137         
28138         imageContext.rotate(this.rotate * Math.PI / 180);
28139         
28140         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28141         
28142         var canvas = document.createElement("canvas");
28143         
28144         var context = canvas.getContext("2d");
28145                 
28146         canvas.width = this.minWidth;
28147         canvas.height = this.minHeight;
28148
28149         switch (this.rotate) {
28150             case 0 :
28151                 
28152                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28153                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28154                 
28155                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28156                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28157                 
28158                 var targetWidth = this.minWidth - 2 * x;
28159                 var targetHeight = this.minHeight - 2 * y;
28160                 
28161                 var scale = 1;
28162                 
28163                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28164                     scale = targetWidth / width;
28165                 }
28166                 
28167                 if(x > 0 && y == 0){
28168                     scale = targetHeight / height;
28169                 }
28170                 
28171                 if(x > 0 && y > 0){
28172                     scale = targetWidth / width;
28173                     
28174                     if(width < height){
28175                         scale = targetHeight / height;
28176                     }
28177                 }
28178                 
28179                 context.scale(scale, scale);
28180                 
28181                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28182                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28183
28184                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28185                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28186
28187                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28188                 
28189                 break;
28190             case 90 : 
28191                 
28192                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28193                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (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) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28228                 
28229                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28230                 
28231                 break;
28232             case 180 :
28233                 
28234                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28235                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28236                 
28237                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28238                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28239                 
28240                 var targetWidth = this.minWidth - 2 * x;
28241                 var targetHeight = this.minHeight - 2 * y;
28242                 
28243                 var scale = 1;
28244                 
28245                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28246                     scale = targetWidth / width;
28247                 }
28248                 
28249                 if(x > 0 && y == 0){
28250                     scale = targetHeight / height;
28251                 }
28252                 
28253                 if(x > 0 && y > 0){
28254                     scale = targetWidth / width;
28255                     
28256                     if(width < height){
28257                         scale = targetHeight / height;
28258                     }
28259                 }
28260                 
28261                 context.scale(scale, scale);
28262                 
28263                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28264                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28265
28266                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28267                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28268
28269                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28270                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28271                 
28272                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28273                 
28274                 break;
28275             case 270 :
28276                 
28277                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28278                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28279                 
28280                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28281                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28282                 
28283                 var targetWidth = this.minWidth - 2 * x;
28284                 var targetHeight = this.minHeight - 2 * y;
28285                 
28286                 var scale = 1;
28287                 
28288                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28289                     scale = targetWidth / width;
28290                 }
28291                 
28292                 if(x > 0 && y == 0){
28293                     scale = targetHeight / height;
28294                 }
28295                 
28296                 if(x > 0 && y > 0){
28297                     scale = targetWidth / width;
28298                     
28299                     if(width < height){
28300                         scale = targetHeight / height;
28301                     }
28302                 }
28303                 
28304                 context.scale(scale, scale);
28305                 
28306                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28307                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28308
28309                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28310                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28311                 
28312                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28313                 
28314                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28315                 
28316                 break;
28317             default : 
28318                 break;
28319         }
28320         
28321         this.cropData = canvas.toDataURL(this.cropType);
28322         
28323         if(this.fireEvent('crop', this, this.cropData) !== false){
28324             this.process(this.file, this.cropData);
28325         }
28326         
28327         return;
28328         
28329     },
28330     
28331     setThumbBoxSize : function()
28332     {
28333         var width, height;
28334         
28335         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28336             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28337             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28338             
28339             this.minWidth = width;
28340             this.minHeight = height;
28341             
28342             if(this.rotate == 90 || this.rotate == 270){
28343                 this.minWidth = height;
28344                 this.minHeight = width;
28345             }
28346         }
28347         
28348         height = 300;
28349         width = Math.ceil(this.minWidth * height / this.minHeight);
28350         
28351         if(this.minWidth > this.minHeight){
28352             width = 300;
28353             height = Math.ceil(this.minHeight * width / this.minWidth);
28354         }
28355         
28356         this.thumbEl.setStyle({
28357             width : width + 'px',
28358             height : height + 'px'
28359         });
28360
28361         return;
28362             
28363     },
28364     
28365     setThumbBoxPosition : function()
28366     {
28367         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28368         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28369         
28370         this.thumbEl.setLeft(x);
28371         this.thumbEl.setTop(y);
28372         
28373     },
28374     
28375     baseRotateLevel : function()
28376     {
28377         this.baseRotate = 1;
28378         
28379         if(
28380                 typeof(this.exif) != 'undefined' &&
28381                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28382                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28383         ){
28384             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28385         }
28386         
28387         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28388         
28389     },
28390     
28391     baseScaleLevel : function()
28392     {
28393         var width, height;
28394         
28395         if(this.isDocument){
28396             
28397             if(this.baseRotate == 6 || this.baseRotate == 8){
28398             
28399                 height = this.thumbEl.getHeight();
28400                 this.baseScale = height / this.imageEl.OriginWidth;
28401
28402                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28403                     width = this.thumbEl.getWidth();
28404                     this.baseScale = width / this.imageEl.OriginHeight;
28405                 }
28406
28407                 return;
28408             }
28409
28410             height = this.thumbEl.getHeight();
28411             this.baseScale = height / this.imageEl.OriginHeight;
28412
28413             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28414                 width = this.thumbEl.getWidth();
28415                 this.baseScale = width / this.imageEl.OriginWidth;
28416             }
28417
28418             return;
28419         }
28420         
28421         if(this.baseRotate == 6 || this.baseRotate == 8){
28422             
28423             width = this.thumbEl.getHeight();
28424             this.baseScale = width / this.imageEl.OriginHeight;
28425             
28426             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28427                 height = this.thumbEl.getWidth();
28428                 this.baseScale = height / this.imageEl.OriginHeight;
28429             }
28430             
28431             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28432                 height = this.thumbEl.getWidth();
28433                 this.baseScale = height / this.imageEl.OriginHeight;
28434                 
28435                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28436                     width = this.thumbEl.getHeight();
28437                     this.baseScale = width / this.imageEl.OriginWidth;
28438                 }
28439             }
28440             
28441             return;
28442         }
28443         
28444         width = this.thumbEl.getWidth();
28445         this.baseScale = width / this.imageEl.OriginWidth;
28446         
28447         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28448             height = this.thumbEl.getHeight();
28449             this.baseScale = height / this.imageEl.OriginHeight;
28450         }
28451         
28452         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28453             
28454             height = this.thumbEl.getHeight();
28455             this.baseScale = height / this.imageEl.OriginHeight;
28456             
28457             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28458                 width = this.thumbEl.getWidth();
28459                 this.baseScale = width / this.imageEl.OriginWidth;
28460             }
28461             
28462         }
28463         
28464         return;
28465     },
28466     
28467     getScaleLevel : function()
28468     {
28469         return this.baseScale * Math.pow(1.1, this.scale);
28470     },
28471     
28472     onTouchStart : function(e)
28473     {
28474         if(!this.canvasLoaded){
28475             this.beforeSelectFile(e);
28476             return;
28477         }
28478         
28479         var touches = e.browserEvent.touches;
28480         
28481         if(!touches){
28482             return;
28483         }
28484         
28485         if(touches.length == 1){
28486             this.onMouseDown(e);
28487             return;
28488         }
28489         
28490         if(touches.length != 2){
28491             return;
28492         }
28493         
28494         var coords = [];
28495         
28496         for(var i = 0, finger; finger = touches[i]; i++){
28497             coords.push(finger.pageX, finger.pageY);
28498         }
28499         
28500         var x = Math.pow(coords[0] - coords[2], 2);
28501         var y = Math.pow(coords[1] - coords[3], 2);
28502         
28503         this.startDistance = Math.sqrt(x + y);
28504         
28505         this.startScale = this.scale;
28506         
28507         this.pinching = true;
28508         this.dragable = false;
28509         
28510     },
28511     
28512     onTouchMove : function(e)
28513     {
28514         if(!this.pinching && !this.dragable){
28515             return;
28516         }
28517         
28518         var touches = e.browserEvent.touches;
28519         
28520         if(!touches){
28521             return;
28522         }
28523         
28524         if(this.dragable){
28525             this.onMouseMove(e);
28526             return;
28527         }
28528         
28529         var coords = [];
28530         
28531         for(var i = 0, finger; finger = touches[i]; i++){
28532             coords.push(finger.pageX, finger.pageY);
28533         }
28534         
28535         var x = Math.pow(coords[0] - coords[2], 2);
28536         var y = Math.pow(coords[1] - coords[3], 2);
28537         
28538         this.endDistance = Math.sqrt(x + y);
28539         
28540         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28541         
28542         if(!this.zoomable()){
28543             this.scale = this.startScale;
28544             return;
28545         }
28546         
28547         this.draw();
28548         
28549     },
28550     
28551     onTouchEnd : function(e)
28552     {
28553         this.pinching = false;
28554         this.dragable = false;
28555         
28556     },
28557     
28558     process : function(file, crop)
28559     {
28560         if(this.loadMask){
28561             this.maskEl.mask(this.loadingText);
28562         }
28563         
28564         this.xhr = new XMLHttpRequest();
28565         
28566         file.xhr = this.xhr;
28567
28568         this.xhr.open(this.method, this.url, true);
28569         
28570         var headers = {
28571             "Accept": "application/json",
28572             "Cache-Control": "no-cache",
28573             "X-Requested-With": "XMLHttpRequest"
28574         };
28575         
28576         for (var headerName in headers) {
28577             var headerValue = headers[headerName];
28578             if (headerValue) {
28579                 this.xhr.setRequestHeader(headerName, headerValue);
28580             }
28581         }
28582         
28583         var _this = this;
28584         
28585         this.xhr.onload = function()
28586         {
28587             _this.xhrOnLoad(_this.xhr);
28588         }
28589         
28590         this.xhr.onerror = function()
28591         {
28592             _this.xhrOnError(_this.xhr);
28593         }
28594         
28595         var formData = new FormData();
28596
28597         formData.append('returnHTML', 'NO');
28598         
28599         if(crop){
28600             formData.append('crop', crop);
28601         }
28602         
28603         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28604             formData.append(this.paramName, file, file.name);
28605         }
28606         
28607         if(typeof(file.filename) != 'undefined'){
28608             formData.append('filename', file.filename);
28609         }
28610         
28611         if(typeof(file.mimetype) != 'undefined'){
28612             formData.append('mimetype', file.mimetype);
28613         }
28614         
28615         if(this.fireEvent('arrange', this, formData) != false){
28616             this.xhr.send(formData);
28617         };
28618     },
28619     
28620     xhrOnLoad : function(xhr)
28621     {
28622         if(this.loadMask){
28623             this.maskEl.unmask();
28624         }
28625         
28626         if (xhr.readyState !== 4) {
28627             this.fireEvent('exception', this, xhr);
28628             return;
28629         }
28630
28631         var response = Roo.decode(xhr.responseText);
28632         
28633         if(!response.success){
28634             this.fireEvent('exception', this, xhr);
28635             return;
28636         }
28637         
28638         var response = Roo.decode(xhr.responseText);
28639         
28640         this.fireEvent('upload', this, response);
28641         
28642     },
28643     
28644     xhrOnError : function()
28645     {
28646         if(this.loadMask){
28647             this.maskEl.unmask();
28648         }
28649         
28650         Roo.log('xhr on error');
28651         
28652         var response = Roo.decode(xhr.responseText);
28653           
28654         Roo.log(response);
28655         
28656     },
28657     
28658     prepare : function(file)
28659     {   
28660         if(this.loadMask){
28661             this.maskEl.mask(this.loadingText);
28662         }
28663         
28664         this.file = false;
28665         this.exif = {};
28666         
28667         if(typeof(file) === 'string'){
28668             this.loadCanvas(file);
28669             return;
28670         }
28671         
28672         if(!file || !this.urlAPI){
28673             return;
28674         }
28675         
28676         this.file = file;
28677         this.cropType = file.type;
28678         
28679         var _this = this;
28680         
28681         if(this.fireEvent('prepare', this, this.file) != false){
28682             
28683             var reader = new FileReader();
28684             
28685             reader.onload = function (e) {
28686                 if (e.target.error) {
28687                     Roo.log(e.target.error);
28688                     return;
28689                 }
28690                 
28691                 var buffer = e.target.result,
28692                     dataView = new DataView(buffer),
28693                     offset = 2,
28694                     maxOffset = dataView.byteLength - 4,
28695                     markerBytes,
28696                     markerLength;
28697                 
28698                 if (dataView.getUint16(0) === 0xffd8) {
28699                     while (offset < maxOffset) {
28700                         markerBytes = dataView.getUint16(offset);
28701                         
28702                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28703                             markerLength = dataView.getUint16(offset + 2) + 2;
28704                             if (offset + markerLength > dataView.byteLength) {
28705                                 Roo.log('Invalid meta data: Invalid segment size.');
28706                                 break;
28707                             }
28708                             
28709                             if(markerBytes == 0xffe1){
28710                                 _this.parseExifData(
28711                                     dataView,
28712                                     offset,
28713                                     markerLength
28714                                 );
28715                             }
28716                             
28717                             offset += markerLength;
28718                             
28719                             continue;
28720                         }
28721                         
28722                         break;
28723                     }
28724                     
28725                 }
28726                 
28727                 var url = _this.urlAPI.createObjectURL(_this.file);
28728                 
28729                 _this.loadCanvas(url);
28730                 
28731                 return;
28732             }
28733             
28734             reader.readAsArrayBuffer(this.file);
28735             
28736         }
28737         
28738     },
28739     
28740     parseExifData : function(dataView, offset, length)
28741     {
28742         var tiffOffset = offset + 10,
28743             littleEndian,
28744             dirOffset;
28745     
28746         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28747             // No Exif data, might be XMP data instead
28748             return;
28749         }
28750         
28751         // Check for the ASCII code for "Exif" (0x45786966):
28752         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28753             // No Exif data, might be XMP data instead
28754             return;
28755         }
28756         if (tiffOffset + 8 > dataView.byteLength) {
28757             Roo.log('Invalid Exif data: Invalid segment size.');
28758             return;
28759         }
28760         // Check for the two null bytes:
28761         if (dataView.getUint16(offset + 8) !== 0x0000) {
28762             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28763             return;
28764         }
28765         // Check the byte alignment:
28766         switch (dataView.getUint16(tiffOffset)) {
28767         case 0x4949:
28768             littleEndian = true;
28769             break;
28770         case 0x4D4D:
28771             littleEndian = false;
28772             break;
28773         default:
28774             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28775             return;
28776         }
28777         // Check for the TIFF tag marker (0x002A):
28778         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28779             Roo.log('Invalid Exif data: Missing TIFF marker.');
28780             return;
28781         }
28782         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28783         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28784         
28785         this.parseExifTags(
28786             dataView,
28787             tiffOffset,
28788             tiffOffset + dirOffset,
28789             littleEndian
28790         );
28791     },
28792     
28793     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28794     {
28795         var tagsNumber,
28796             dirEndOffset,
28797             i;
28798         if (dirOffset + 6 > dataView.byteLength) {
28799             Roo.log('Invalid Exif data: Invalid directory offset.');
28800             return;
28801         }
28802         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28803         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28804         if (dirEndOffset + 4 > dataView.byteLength) {
28805             Roo.log('Invalid Exif data: Invalid directory size.');
28806             return;
28807         }
28808         for (i = 0; i < tagsNumber; i += 1) {
28809             this.parseExifTag(
28810                 dataView,
28811                 tiffOffset,
28812                 dirOffset + 2 + 12 * i, // tag offset
28813                 littleEndian
28814             );
28815         }
28816         // Return the offset to the next directory:
28817         return dataView.getUint32(dirEndOffset, littleEndian);
28818     },
28819     
28820     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28821     {
28822         var tag = dataView.getUint16(offset, littleEndian);
28823         
28824         this.exif[tag] = this.getExifValue(
28825             dataView,
28826             tiffOffset,
28827             offset,
28828             dataView.getUint16(offset + 2, littleEndian), // tag type
28829             dataView.getUint32(offset + 4, littleEndian), // tag length
28830             littleEndian
28831         );
28832     },
28833     
28834     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28835     {
28836         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28837             tagSize,
28838             dataOffset,
28839             values,
28840             i,
28841             str,
28842             c;
28843     
28844         if (!tagType) {
28845             Roo.log('Invalid Exif data: Invalid tag type.');
28846             return;
28847         }
28848         
28849         tagSize = tagType.size * length;
28850         // Determine if the value is contained in the dataOffset bytes,
28851         // or if the value at the dataOffset is a pointer to the actual data:
28852         dataOffset = tagSize > 4 ?
28853                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28854         if (dataOffset + tagSize > dataView.byteLength) {
28855             Roo.log('Invalid Exif data: Invalid data offset.');
28856             return;
28857         }
28858         if (length === 1) {
28859             return tagType.getValue(dataView, dataOffset, littleEndian);
28860         }
28861         values = [];
28862         for (i = 0; i < length; i += 1) {
28863             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28864         }
28865         
28866         if (tagType.ascii) {
28867             str = '';
28868             // Concatenate the chars:
28869             for (i = 0; i < values.length; i += 1) {
28870                 c = values[i];
28871                 // Ignore the terminating NULL byte(s):
28872                 if (c === '\u0000') {
28873                     break;
28874                 }
28875                 str += c;
28876             }
28877             return str;
28878         }
28879         return values;
28880     }
28881     
28882 });
28883
28884 Roo.apply(Roo.bootstrap.UploadCropbox, {
28885     tags : {
28886         'Orientation': 0x0112
28887     },
28888     
28889     Orientation: {
28890             1: 0, //'top-left',
28891 //            2: 'top-right',
28892             3: 180, //'bottom-right',
28893 //            4: 'bottom-left',
28894 //            5: 'left-top',
28895             6: 90, //'right-top',
28896 //            7: 'right-bottom',
28897             8: 270 //'left-bottom'
28898     },
28899     
28900     exifTagTypes : {
28901         // byte, 8-bit unsigned int:
28902         1: {
28903             getValue: function (dataView, dataOffset) {
28904                 return dataView.getUint8(dataOffset);
28905             },
28906             size: 1
28907         },
28908         // ascii, 8-bit byte:
28909         2: {
28910             getValue: function (dataView, dataOffset) {
28911                 return String.fromCharCode(dataView.getUint8(dataOffset));
28912             },
28913             size: 1,
28914             ascii: true
28915         },
28916         // short, 16 bit int:
28917         3: {
28918             getValue: function (dataView, dataOffset, littleEndian) {
28919                 return dataView.getUint16(dataOffset, littleEndian);
28920             },
28921             size: 2
28922         },
28923         // long, 32 bit int:
28924         4: {
28925             getValue: function (dataView, dataOffset, littleEndian) {
28926                 return dataView.getUint32(dataOffset, littleEndian);
28927             },
28928             size: 4
28929         },
28930         // rational = two long values, first is numerator, second is denominator:
28931         5: {
28932             getValue: function (dataView, dataOffset, littleEndian) {
28933                 return dataView.getUint32(dataOffset, littleEndian) /
28934                     dataView.getUint32(dataOffset + 4, littleEndian);
28935             },
28936             size: 8
28937         },
28938         // slong, 32 bit signed int:
28939         9: {
28940             getValue: function (dataView, dataOffset, littleEndian) {
28941                 return dataView.getInt32(dataOffset, littleEndian);
28942             },
28943             size: 4
28944         },
28945         // srational, two slongs, first is numerator, second is denominator:
28946         10: {
28947             getValue: function (dataView, dataOffset, littleEndian) {
28948                 return dataView.getInt32(dataOffset, littleEndian) /
28949                     dataView.getInt32(dataOffset + 4, littleEndian);
28950             },
28951             size: 8
28952         }
28953     },
28954     
28955     footer : {
28956         STANDARD : [
28957             {
28958                 tag : 'div',
28959                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28960                 action : 'rotate-left',
28961                 cn : [
28962                     {
28963                         tag : 'button',
28964                         cls : 'btn btn-default',
28965                         html : '<i class="fa fa-undo"></i>'
28966                     }
28967                 ]
28968             },
28969             {
28970                 tag : 'div',
28971                 cls : 'btn-group roo-upload-cropbox-picture',
28972                 action : 'picture',
28973                 cn : [
28974                     {
28975                         tag : 'button',
28976                         cls : 'btn btn-default',
28977                         html : '<i class="fa fa-picture-o"></i>'
28978                     }
28979                 ]
28980             },
28981             {
28982                 tag : 'div',
28983                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28984                 action : 'rotate-right',
28985                 cn : [
28986                     {
28987                         tag : 'button',
28988                         cls : 'btn btn-default',
28989                         html : '<i class="fa fa-repeat"></i>'
28990                     }
28991                 ]
28992             }
28993         ],
28994         DOCUMENT : [
28995             {
28996                 tag : 'div',
28997                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28998                 action : 'rotate-left',
28999                 cn : [
29000                     {
29001                         tag : 'button',
29002                         cls : 'btn btn-default',
29003                         html : '<i class="fa fa-undo"></i>'
29004                     }
29005                 ]
29006             },
29007             {
29008                 tag : 'div',
29009                 cls : 'btn-group roo-upload-cropbox-download',
29010                 action : 'download',
29011                 cn : [
29012                     {
29013                         tag : 'button',
29014                         cls : 'btn btn-default',
29015                         html : '<i class="fa fa-download"></i>'
29016                     }
29017                 ]
29018             },
29019             {
29020                 tag : 'div',
29021                 cls : 'btn-group roo-upload-cropbox-crop',
29022                 action : 'crop',
29023                 cn : [
29024                     {
29025                         tag : 'button',
29026                         cls : 'btn btn-default',
29027                         html : '<i class="fa fa-crop"></i>'
29028                     }
29029                 ]
29030             },
29031             {
29032                 tag : 'div',
29033                 cls : 'btn-group roo-upload-cropbox-trash',
29034                 action : 'trash',
29035                 cn : [
29036                     {
29037                         tag : 'button',
29038                         cls : 'btn btn-default',
29039                         html : '<i class="fa fa-trash"></i>'
29040                     }
29041                 ]
29042             },
29043             {
29044                 tag : 'div',
29045                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29046                 action : 'rotate-right',
29047                 cn : [
29048                     {
29049                         tag : 'button',
29050                         cls : 'btn btn-default',
29051                         html : '<i class="fa fa-repeat"></i>'
29052                     }
29053                 ]
29054             }
29055         ],
29056         ROTATOR : [
29057             {
29058                 tag : 'div',
29059                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29060                 action : 'rotate-left',
29061                 cn : [
29062                     {
29063                         tag : 'button',
29064                         cls : 'btn btn-default',
29065                         html : '<i class="fa fa-undo"></i>'
29066                     }
29067                 ]
29068             },
29069             {
29070                 tag : 'div',
29071                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29072                 action : 'rotate-right',
29073                 cn : [
29074                     {
29075                         tag : 'button',
29076                         cls : 'btn btn-default',
29077                         html : '<i class="fa fa-repeat"></i>'
29078                     }
29079                 ]
29080             }
29081         ]
29082     }
29083 });
29084
29085 /*
29086 * Licence: LGPL
29087 */
29088
29089 /**
29090  * @class Roo.bootstrap.DocumentManager
29091  * @extends Roo.bootstrap.Component
29092  * Bootstrap DocumentManager class
29093  * @cfg {String} paramName default 'imageUpload'
29094  * @cfg {String} toolTipName default 'filename'
29095  * @cfg {String} method default POST
29096  * @cfg {String} url action url
29097  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29098  * @cfg {Boolean} multiple multiple upload default true
29099  * @cfg {Number} thumbSize default 300
29100  * @cfg {String} fieldLabel
29101  * @cfg {Number} labelWidth default 4
29102  * @cfg {String} labelAlign (left|top) default left
29103  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29104 * @cfg {Number} labellg set the width of label (1-12)
29105  * @cfg {Number} labelmd set the width of label (1-12)
29106  * @cfg {Number} labelsm set the width of label (1-12)
29107  * @cfg {Number} labelxs set the width of label (1-12)
29108  * 
29109  * @constructor
29110  * Create a new DocumentManager
29111  * @param {Object} config The config object
29112  */
29113
29114 Roo.bootstrap.DocumentManager = function(config){
29115     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29116     
29117     this.files = [];
29118     this.delegates = [];
29119     
29120     this.addEvents({
29121         /**
29122          * @event initial
29123          * Fire when initial the DocumentManager
29124          * @param {Roo.bootstrap.DocumentManager} this
29125          */
29126         "initial" : true,
29127         /**
29128          * @event inspect
29129          * inspect selected file
29130          * @param {Roo.bootstrap.DocumentManager} this
29131          * @param {File} file
29132          */
29133         "inspect" : true,
29134         /**
29135          * @event exception
29136          * Fire when xhr load exception
29137          * @param {Roo.bootstrap.DocumentManager} this
29138          * @param {XMLHttpRequest} xhr
29139          */
29140         "exception" : true,
29141         /**
29142          * @event afterupload
29143          * Fire when xhr load exception
29144          * @param {Roo.bootstrap.DocumentManager} this
29145          * @param {XMLHttpRequest} xhr
29146          */
29147         "afterupload" : true,
29148         /**
29149          * @event prepare
29150          * prepare the form data
29151          * @param {Roo.bootstrap.DocumentManager} this
29152          * @param {Object} formData
29153          */
29154         "prepare" : true,
29155         /**
29156          * @event remove
29157          * Fire when remove the file
29158          * @param {Roo.bootstrap.DocumentManager} this
29159          * @param {Object} file
29160          */
29161         "remove" : true,
29162         /**
29163          * @event refresh
29164          * Fire after refresh the file
29165          * @param {Roo.bootstrap.DocumentManager} this
29166          */
29167         "refresh" : true,
29168         /**
29169          * @event click
29170          * Fire after click the image
29171          * @param {Roo.bootstrap.DocumentManager} this
29172          * @param {Object} file
29173          */
29174         "click" : true,
29175         /**
29176          * @event edit
29177          * Fire when upload a image and editable set to true
29178          * @param {Roo.bootstrap.DocumentManager} this
29179          * @param {Object} file
29180          */
29181         "edit" : true,
29182         /**
29183          * @event beforeselectfile
29184          * Fire before select file
29185          * @param {Roo.bootstrap.DocumentManager} this
29186          */
29187         "beforeselectfile" : true,
29188         /**
29189          * @event process
29190          * Fire before process file
29191          * @param {Roo.bootstrap.DocumentManager} this
29192          * @param {Object} file
29193          */
29194         "process" : true,
29195         /**
29196          * @event previewrendered
29197          * Fire when preview rendered
29198          * @param {Roo.bootstrap.DocumentManager} this
29199          * @param {Object} file
29200          */
29201         "previewrendered" : true,
29202         /**
29203          */
29204         "previewResize" : true
29205         
29206     });
29207 };
29208
29209 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29210     
29211     boxes : 0,
29212     inputName : '',
29213     thumbSize : 300,
29214     multiple : true,
29215     files : false,
29216     method : 'POST',
29217     url : '',
29218     paramName : 'imageUpload',
29219     toolTipName : 'filename',
29220     fieldLabel : '',
29221     labelWidth : 4,
29222     labelAlign : 'left',
29223     editable : true,
29224     delegates : false,
29225     xhr : false, 
29226     
29227     labellg : 0,
29228     labelmd : 0,
29229     labelsm : 0,
29230     labelxs : 0,
29231     
29232     getAutoCreate : function()
29233     {   
29234         var managerWidget = {
29235             tag : 'div',
29236             cls : 'roo-document-manager',
29237             cn : [
29238                 {
29239                     tag : 'input',
29240                     cls : 'roo-document-manager-selector',
29241                     type : 'file'
29242                 },
29243                 {
29244                     tag : 'div',
29245                     cls : 'roo-document-manager-uploader',
29246                     cn : [
29247                         {
29248                             tag : 'div',
29249                             cls : 'roo-document-manager-upload-btn',
29250                             html : '<i class="fa fa-plus"></i>'
29251                         }
29252                     ]
29253                     
29254                 }
29255             ]
29256         };
29257         
29258         var content = [
29259             {
29260                 tag : 'div',
29261                 cls : 'column col-md-12',
29262                 cn : managerWidget
29263             }
29264         ];
29265         
29266         if(this.fieldLabel.length){
29267             
29268             content = [
29269                 {
29270                     tag : 'div',
29271                     cls : 'column col-md-12',
29272                     html : this.fieldLabel
29273                 },
29274                 {
29275                     tag : 'div',
29276                     cls : 'column col-md-12',
29277                     cn : managerWidget
29278                 }
29279             ];
29280
29281             if(this.labelAlign == 'left'){
29282                 content = [
29283                     {
29284                         tag : 'div',
29285                         cls : 'column',
29286                         html : this.fieldLabel
29287                     },
29288                     {
29289                         tag : 'div',
29290                         cls : 'column',
29291                         cn : managerWidget
29292                     }
29293                 ];
29294                 
29295                 if(this.labelWidth > 12){
29296                     content[0].style = "width: " + this.labelWidth + 'px';
29297                 }
29298
29299                 if(this.labelWidth < 13 && this.labelmd == 0){
29300                     this.labelmd = this.labelWidth;
29301                 }
29302
29303                 if(this.labellg > 0){
29304                     content[0].cls += ' col-lg-' + this.labellg;
29305                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29306                 }
29307
29308                 if(this.labelmd > 0){
29309                     content[0].cls += ' col-md-' + this.labelmd;
29310                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29311                 }
29312
29313                 if(this.labelsm > 0){
29314                     content[0].cls += ' col-sm-' + this.labelsm;
29315                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29316                 }
29317
29318                 if(this.labelxs > 0){
29319                     content[0].cls += ' col-xs-' + this.labelxs;
29320                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29321                 }
29322                 
29323             }
29324         }
29325         
29326         var cfg = {
29327             tag : 'div',
29328             cls : 'row clearfix',
29329             cn : content
29330         };
29331         
29332         return cfg;
29333         
29334     },
29335     
29336     initEvents : function()
29337     {
29338         this.managerEl = this.el.select('.roo-document-manager', true).first();
29339         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29340         
29341         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29342         this.selectorEl.hide();
29343         
29344         if(this.multiple){
29345             this.selectorEl.attr('multiple', 'multiple');
29346         }
29347         
29348         this.selectorEl.on('change', this.onFileSelected, this);
29349         
29350         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29351         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29352         
29353         this.uploader.on('click', this.onUploaderClick, this);
29354         
29355         this.renderProgressDialog();
29356         
29357         var _this = this;
29358         
29359         window.addEventListener("resize", function() { _this.refresh(); } );
29360         
29361         this.fireEvent('initial', this);
29362     },
29363     
29364     renderProgressDialog : function()
29365     {
29366         var _this = this;
29367         
29368         this.progressDialog = new Roo.bootstrap.Modal({
29369             cls : 'roo-document-manager-progress-dialog',
29370             allow_close : false,
29371             animate : false,
29372             title : '',
29373             buttons : [
29374                 {
29375                     name  :'cancel',
29376                     weight : 'danger',
29377                     html : 'Cancel'
29378                 }
29379             ], 
29380             listeners : { 
29381                 btnclick : function() {
29382                     _this.uploadCancel();
29383                     this.hide();
29384                 }
29385             }
29386         });
29387          
29388         this.progressDialog.render(Roo.get(document.body));
29389          
29390         this.progress = new Roo.bootstrap.Progress({
29391             cls : 'roo-document-manager-progress',
29392             active : true,
29393             striped : true
29394         });
29395         
29396         this.progress.render(this.progressDialog.getChildContainer());
29397         
29398         this.progressBar = new Roo.bootstrap.ProgressBar({
29399             cls : 'roo-document-manager-progress-bar',
29400             aria_valuenow : 0,
29401             aria_valuemin : 0,
29402             aria_valuemax : 12,
29403             panel : 'success'
29404         });
29405         
29406         this.progressBar.render(this.progress.getChildContainer());
29407     },
29408     
29409     onUploaderClick : function(e)
29410     {
29411         e.preventDefault();
29412      
29413         if(this.fireEvent('beforeselectfile', this) != false){
29414             this.selectorEl.dom.click();
29415         }
29416         
29417     },
29418     
29419     onFileSelected : function(e)
29420     {
29421         e.preventDefault();
29422         
29423         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29424             return;
29425         }
29426         
29427         Roo.each(this.selectorEl.dom.files, function(file){
29428             if(this.fireEvent('inspect', this, file) != false){
29429                 this.files.push(file);
29430             }
29431         }, this);
29432         
29433         this.queue();
29434         
29435     },
29436     
29437     queue : function()
29438     {
29439         this.selectorEl.dom.value = '';
29440         
29441         if(!this.files || !this.files.length){
29442             return;
29443         }
29444         
29445         if(this.boxes > 0 && this.files.length > this.boxes){
29446             this.files = this.files.slice(0, this.boxes);
29447         }
29448         
29449         this.uploader.show();
29450         
29451         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29452             this.uploader.hide();
29453         }
29454         
29455         var _this = this;
29456         
29457         var files = [];
29458         
29459         var docs = [];
29460         
29461         Roo.each(this.files, function(file){
29462             
29463             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29464                 var f = this.renderPreview(file);
29465                 files.push(f);
29466                 return;
29467             }
29468             
29469             if(file.type.indexOf('image') != -1){
29470                 this.delegates.push(
29471                     (function(){
29472                         _this.process(file);
29473                     }).createDelegate(this)
29474                 );
29475         
29476                 return;
29477             }
29478             
29479             docs.push(
29480                 (function(){
29481                     _this.process(file);
29482                 }).createDelegate(this)
29483             );
29484             
29485         }, this);
29486         
29487         this.files = files;
29488         
29489         this.delegates = this.delegates.concat(docs);
29490         
29491         if(!this.delegates.length){
29492             this.refresh();
29493             return;
29494         }
29495         
29496         this.progressBar.aria_valuemax = this.delegates.length;
29497         
29498         this.arrange();
29499         
29500         return;
29501     },
29502     
29503     arrange : function()
29504     {
29505         if(!this.delegates.length){
29506             this.progressDialog.hide();
29507             this.refresh();
29508             return;
29509         }
29510         
29511         var delegate = this.delegates.shift();
29512         
29513         this.progressDialog.show();
29514         
29515         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29516         
29517         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29518         
29519         delegate();
29520     },
29521     
29522     refresh : function()
29523     {
29524         this.uploader.show();
29525         
29526         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29527             this.uploader.hide();
29528         }
29529         
29530         Roo.isTouch ? this.closable(false) : this.closable(true);
29531         
29532         this.fireEvent('refresh', this);
29533     },
29534     
29535     onRemove : function(e, el, o)
29536     {
29537         e.preventDefault();
29538         
29539         this.fireEvent('remove', this, o);
29540         
29541     },
29542     
29543     remove : function(o)
29544     {
29545         var files = [];
29546         
29547         Roo.each(this.files, function(file){
29548             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29549                 files.push(file);
29550                 return;
29551             }
29552
29553             o.target.remove();
29554
29555         }, this);
29556         
29557         this.files = files;
29558         
29559         this.refresh();
29560     },
29561     
29562     clear : function()
29563     {
29564         Roo.each(this.files, function(file){
29565             if(!file.target){
29566                 return;
29567             }
29568             
29569             file.target.remove();
29570
29571         }, this);
29572         
29573         this.files = [];
29574         
29575         this.refresh();
29576     },
29577     
29578     onClick : function(e, el, o)
29579     {
29580         e.preventDefault();
29581         
29582         this.fireEvent('click', this, o);
29583         
29584     },
29585     
29586     closable : function(closable)
29587     {
29588         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29589             
29590             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29591             
29592             if(closable){
29593                 el.show();
29594                 return;
29595             }
29596             
29597             el.hide();
29598             
29599         }, this);
29600     },
29601     
29602     xhrOnLoad : function(xhr)
29603     {
29604         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29605             el.remove();
29606         }, this);
29607         
29608         if (xhr.readyState !== 4) {
29609             this.arrange();
29610             this.fireEvent('exception', this, xhr);
29611             return;
29612         }
29613
29614         var response = Roo.decode(xhr.responseText);
29615         
29616         if(!response.success){
29617             this.arrange();
29618             this.fireEvent('exception', this, xhr);
29619             return;
29620         }
29621         
29622         var file = this.renderPreview(response.data);
29623         
29624         this.files.push(file);
29625         
29626         this.arrange();
29627         
29628         this.fireEvent('afterupload', this, xhr);
29629         
29630     },
29631     
29632     xhrOnError : function(xhr)
29633     {
29634         Roo.log('xhr on error');
29635         
29636         var response = Roo.decode(xhr.responseText);
29637           
29638         Roo.log(response);
29639         
29640         this.arrange();
29641     },
29642     
29643     process : function(file)
29644     {
29645         if(this.fireEvent('process', this, file) !== false){
29646             if(this.editable && file.type.indexOf('image') != -1){
29647                 this.fireEvent('edit', this, file);
29648                 return;
29649             }
29650
29651             this.uploadStart(file, false);
29652
29653             return;
29654         }
29655         
29656     },
29657     
29658     uploadStart : function(file, crop)
29659     {
29660         this.xhr = new XMLHttpRequest();
29661         
29662         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29663             this.arrange();
29664             return;
29665         }
29666         
29667         file.xhr = this.xhr;
29668             
29669         this.managerEl.createChild({
29670             tag : 'div',
29671             cls : 'roo-document-manager-loading',
29672             cn : [
29673                 {
29674                     tag : 'div',
29675                     tooltip : file.name,
29676                     cls : 'roo-document-manager-thumb',
29677                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29678                 }
29679             ]
29680
29681         });
29682
29683         this.xhr.open(this.method, this.url, true);
29684         
29685         var headers = {
29686             "Accept": "application/json",
29687             "Cache-Control": "no-cache",
29688             "X-Requested-With": "XMLHttpRequest"
29689         };
29690         
29691         for (var headerName in headers) {
29692             var headerValue = headers[headerName];
29693             if (headerValue) {
29694                 this.xhr.setRequestHeader(headerName, headerValue);
29695             }
29696         }
29697         
29698         var _this = this;
29699         
29700         this.xhr.onload = function()
29701         {
29702             _this.xhrOnLoad(_this.xhr);
29703         }
29704         
29705         this.xhr.onerror = function()
29706         {
29707             _this.xhrOnError(_this.xhr);
29708         }
29709         
29710         var formData = new FormData();
29711
29712         formData.append('returnHTML', 'NO');
29713         
29714         if(crop){
29715             formData.append('crop', crop);
29716         }
29717         
29718         formData.append(this.paramName, file, file.name);
29719         
29720         var options = {
29721             file : file, 
29722             manually : false
29723         };
29724         
29725         if(this.fireEvent('prepare', this, formData, options) != false){
29726             
29727             if(options.manually){
29728                 return;
29729             }
29730             
29731             this.xhr.send(formData);
29732             return;
29733         };
29734         
29735         this.uploadCancel();
29736     },
29737     
29738     uploadCancel : function()
29739     {
29740         if (this.xhr) {
29741             this.xhr.abort();
29742         }
29743         
29744         this.delegates = [];
29745         
29746         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29747             el.remove();
29748         }, this);
29749         
29750         this.arrange();
29751     },
29752     
29753     renderPreview : function(file)
29754     {
29755         if(typeof(file.target) != 'undefined' && file.target){
29756             return file;
29757         }
29758         
29759         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29760         
29761         var previewEl = this.managerEl.createChild({
29762             tag : 'div',
29763             cls : 'roo-document-manager-preview',
29764             cn : [
29765                 {
29766                     tag : 'div',
29767                     tooltip : file[this.toolTipName],
29768                     cls : 'roo-document-manager-thumb',
29769                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29770                 },
29771                 {
29772                     tag : 'button',
29773                     cls : 'close',
29774                     html : '<i class="fa fa-times-circle"></i>'
29775                 }
29776             ]
29777         });
29778
29779         var close = previewEl.select('button.close', true).first();
29780
29781         close.on('click', this.onRemove, this, file);
29782
29783         file.target = previewEl;
29784
29785         var image = previewEl.select('img', true).first();
29786         
29787         var _this = this;
29788         
29789         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29790         
29791         image.on('click', this.onClick, this, file);
29792         
29793         this.fireEvent('previewrendered', this, file);
29794         
29795         return file;
29796         
29797     },
29798     
29799     onPreviewLoad : function(file, image)
29800     {
29801         if(typeof(file.target) == 'undefined' || !file.target){
29802             return;
29803         }
29804         
29805         var width = image.dom.naturalWidth || image.dom.width;
29806         var height = image.dom.naturalHeight || image.dom.height;
29807         
29808         if(!this.previewResize) {
29809             return;
29810         }
29811         
29812         if(width > height){
29813             file.target.addClass('wide');
29814             return;
29815         }
29816         
29817         file.target.addClass('tall');
29818         return;
29819         
29820     },
29821     
29822     uploadFromSource : function(file, crop)
29823     {
29824         this.xhr = new XMLHttpRequest();
29825         
29826         this.managerEl.createChild({
29827             tag : 'div',
29828             cls : 'roo-document-manager-loading',
29829             cn : [
29830                 {
29831                     tag : 'div',
29832                     tooltip : file.name,
29833                     cls : 'roo-document-manager-thumb',
29834                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29835                 }
29836             ]
29837
29838         });
29839
29840         this.xhr.open(this.method, this.url, true);
29841         
29842         var headers = {
29843             "Accept": "application/json",
29844             "Cache-Control": "no-cache",
29845             "X-Requested-With": "XMLHttpRequest"
29846         };
29847         
29848         for (var headerName in headers) {
29849             var headerValue = headers[headerName];
29850             if (headerValue) {
29851                 this.xhr.setRequestHeader(headerName, headerValue);
29852             }
29853         }
29854         
29855         var _this = this;
29856         
29857         this.xhr.onload = function()
29858         {
29859             _this.xhrOnLoad(_this.xhr);
29860         }
29861         
29862         this.xhr.onerror = function()
29863         {
29864             _this.xhrOnError(_this.xhr);
29865         }
29866         
29867         var formData = new FormData();
29868
29869         formData.append('returnHTML', 'NO');
29870         
29871         formData.append('crop', crop);
29872         
29873         if(typeof(file.filename) != 'undefined'){
29874             formData.append('filename', file.filename);
29875         }
29876         
29877         if(typeof(file.mimetype) != 'undefined'){
29878             formData.append('mimetype', file.mimetype);
29879         }
29880         
29881         Roo.log(formData);
29882         
29883         if(this.fireEvent('prepare', this, formData) != false){
29884             this.xhr.send(formData);
29885         };
29886     }
29887 });
29888
29889 /*
29890 * Licence: LGPL
29891 */
29892
29893 /**
29894  * @class Roo.bootstrap.DocumentViewer
29895  * @extends Roo.bootstrap.Component
29896  * Bootstrap DocumentViewer class
29897  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29898  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29899  * 
29900  * @constructor
29901  * Create a new DocumentViewer
29902  * @param {Object} config The config object
29903  */
29904
29905 Roo.bootstrap.DocumentViewer = function(config){
29906     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29907     
29908     this.addEvents({
29909         /**
29910          * @event initial
29911          * Fire after initEvent
29912          * @param {Roo.bootstrap.DocumentViewer} this
29913          */
29914         "initial" : true,
29915         /**
29916          * @event click
29917          * Fire after click
29918          * @param {Roo.bootstrap.DocumentViewer} this
29919          */
29920         "click" : true,
29921         /**
29922          * @event download
29923          * Fire after download button
29924          * @param {Roo.bootstrap.DocumentViewer} this
29925          */
29926         "download" : true,
29927         /**
29928          * @event trash
29929          * Fire after trash button
29930          * @param {Roo.bootstrap.DocumentViewer} this
29931          */
29932         "trash" : true
29933         
29934     });
29935 };
29936
29937 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29938     
29939     showDownload : true,
29940     
29941     showTrash : true,
29942     
29943     getAutoCreate : function()
29944     {
29945         var cfg = {
29946             tag : 'div',
29947             cls : 'roo-document-viewer',
29948             cn : [
29949                 {
29950                     tag : 'div',
29951                     cls : 'roo-document-viewer-body',
29952                     cn : [
29953                         {
29954                             tag : 'div',
29955                             cls : 'roo-document-viewer-thumb',
29956                             cn : [
29957                                 {
29958                                     tag : 'img',
29959                                     cls : 'roo-document-viewer-image'
29960                                 }
29961                             ]
29962                         }
29963                     ]
29964                 },
29965                 {
29966                     tag : 'div',
29967                     cls : 'roo-document-viewer-footer',
29968                     cn : {
29969                         tag : 'div',
29970                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29971                         cn : [
29972                             {
29973                                 tag : 'div',
29974                                 cls : 'btn-group roo-document-viewer-download',
29975                                 cn : [
29976                                     {
29977                                         tag : 'button',
29978                                         cls : 'btn btn-default',
29979                                         html : '<i class="fa fa-download"></i>'
29980                                     }
29981                                 ]
29982                             },
29983                             {
29984                                 tag : 'div',
29985                                 cls : 'btn-group roo-document-viewer-trash',
29986                                 cn : [
29987                                     {
29988                                         tag : 'button',
29989                                         cls : 'btn btn-default',
29990                                         html : '<i class="fa fa-trash"></i>'
29991                                     }
29992                                 ]
29993                             }
29994                         ]
29995                     }
29996                 }
29997             ]
29998         };
29999         
30000         return cfg;
30001     },
30002     
30003     initEvents : function()
30004     {
30005         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30006         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30007         
30008         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30009         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30010         
30011         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30012         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30013         
30014         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30015         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30016         
30017         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30018         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30019         
30020         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30021         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30022         
30023         this.bodyEl.on('click', this.onClick, this);
30024         this.downloadBtn.on('click', this.onDownload, this);
30025         this.trashBtn.on('click', this.onTrash, this);
30026         
30027         this.downloadBtn.hide();
30028         this.trashBtn.hide();
30029         
30030         if(this.showDownload){
30031             this.downloadBtn.show();
30032         }
30033         
30034         if(this.showTrash){
30035             this.trashBtn.show();
30036         }
30037         
30038         if(!this.showDownload && !this.showTrash) {
30039             this.footerEl.hide();
30040         }
30041         
30042     },
30043     
30044     initial : function()
30045     {
30046         this.fireEvent('initial', this);
30047         
30048     },
30049     
30050     onClick : function(e)
30051     {
30052         e.preventDefault();
30053         
30054         this.fireEvent('click', this);
30055     },
30056     
30057     onDownload : function(e)
30058     {
30059         e.preventDefault();
30060         
30061         this.fireEvent('download', this);
30062     },
30063     
30064     onTrash : function(e)
30065     {
30066         e.preventDefault();
30067         
30068         this.fireEvent('trash', this);
30069     }
30070     
30071 });
30072 /*
30073  * - LGPL
30074  *
30075  * nav progress bar
30076  * 
30077  */
30078
30079 /**
30080  * @class Roo.bootstrap.NavProgressBar
30081  * @extends Roo.bootstrap.Component
30082  * Bootstrap NavProgressBar class
30083  * 
30084  * @constructor
30085  * Create a new nav progress bar
30086  * @param {Object} config The config object
30087  */
30088
30089 Roo.bootstrap.NavProgressBar = function(config){
30090     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30091
30092     this.bullets = this.bullets || [];
30093    
30094 //    Roo.bootstrap.NavProgressBar.register(this);
30095      this.addEvents({
30096         /**
30097              * @event changed
30098              * Fires when the active item changes
30099              * @param {Roo.bootstrap.NavProgressBar} this
30100              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30101              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30102          */
30103         'changed': true
30104      });
30105     
30106 };
30107
30108 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30109     
30110     bullets : [],
30111     barItems : [],
30112     
30113     getAutoCreate : function()
30114     {
30115         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30116         
30117         cfg = {
30118             tag : 'div',
30119             cls : 'roo-navigation-bar-group',
30120             cn : [
30121                 {
30122                     tag : 'div',
30123                     cls : 'roo-navigation-top-bar'
30124                 },
30125                 {
30126                     tag : 'div',
30127                     cls : 'roo-navigation-bullets-bar',
30128                     cn : [
30129                         {
30130                             tag : 'ul',
30131                             cls : 'roo-navigation-bar'
30132                         }
30133                     ]
30134                 },
30135                 
30136                 {
30137                     tag : 'div',
30138                     cls : 'roo-navigation-bottom-bar'
30139                 }
30140             ]
30141             
30142         };
30143         
30144         return cfg;
30145         
30146     },
30147     
30148     initEvents: function() 
30149     {
30150         
30151     },
30152     
30153     onRender : function(ct, position) 
30154     {
30155         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30156         
30157         if(this.bullets.length){
30158             Roo.each(this.bullets, function(b){
30159                this.addItem(b);
30160             }, this);
30161         }
30162         
30163         this.format();
30164         
30165     },
30166     
30167     addItem : function(cfg)
30168     {
30169         var item = new Roo.bootstrap.NavProgressItem(cfg);
30170         
30171         item.parentId = this.id;
30172         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30173         
30174         if(cfg.html){
30175             var top = new Roo.bootstrap.Element({
30176                 tag : 'div',
30177                 cls : 'roo-navigation-bar-text'
30178             });
30179             
30180             var bottom = new Roo.bootstrap.Element({
30181                 tag : 'div',
30182                 cls : 'roo-navigation-bar-text'
30183             });
30184             
30185             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30186             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30187             
30188             var topText = new Roo.bootstrap.Element({
30189                 tag : 'span',
30190                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30191             });
30192             
30193             var bottomText = new Roo.bootstrap.Element({
30194                 tag : 'span',
30195                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30196             });
30197             
30198             topText.onRender(top.el, null);
30199             bottomText.onRender(bottom.el, null);
30200             
30201             item.topEl = top;
30202             item.bottomEl = bottom;
30203         }
30204         
30205         this.barItems.push(item);
30206         
30207         return item;
30208     },
30209     
30210     getActive : function()
30211     {
30212         var active = false;
30213         
30214         Roo.each(this.barItems, function(v){
30215             
30216             if (!v.isActive()) {
30217                 return;
30218             }
30219             
30220             active = v;
30221             return false;
30222             
30223         });
30224         
30225         return active;
30226     },
30227     
30228     setActiveItem : function(item)
30229     {
30230         var prev = false;
30231         
30232         Roo.each(this.barItems, function(v){
30233             if (v.rid == item.rid) {
30234                 return ;
30235             }
30236             
30237             if (v.isActive()) {
30238                 v.setActive(false);
30239                 prev = v;
30240             }
30241         });
30242
30243         item.setActive(true);
30244         
30245         this.fireEvent('changed', this, item, prev);
30246     },
30247     
30248     getBarItem: function(rid)
30249     {
30250         var ret = false;
30251         
30252         Roo.each(this.barItems, function(e) {
30253             if (e.rid != rid) {
30254                 return;
30255             }
30256             
30257             ret =  e;
30258             return false;
30259         });
30260         
30261         return ret;
30262     },
30263     
30264     indexOfItem : function(item)
30265     {
30266         var index = false;
30267         
30268         Roo.each(this.barItems, function(v, i){
30269             
30270             if (v.rid != item.rid) {
30271                 return;
30272             }
30273             
30274             index = i;
30275             return false
30276         });
30277         
30278         return index;
30279     },
30280     
30281     setActiveNext : function()
30282     {
30283         var i = this.indexOfItem(this.getActive());
30284         
30285         if (i > this.barItems.length) {
30286             return;
30287         }
30288         
30289         this.setActiveItem(this.barItems[i+1]);
30290     },
30291     
30292     setActivePrev : function()
30293     {
30294         var i = this.indexOfItem(this.getActive());
30295         
30296         if (i  < 1) {
30297             return;
30298         }
30299         
30300         this.setActiveItem(this.barItems[i-1]);
30301     },
30302     
30303     format : function()
30304     {
30305         if(!this.barItems.length){
30306             return;
30307         }
30308      
30309         var width = 100 / this.barItems.length;
30310         
30311         Roo.each(this.barItems, function(i){
30312             i.el.setStyle('width', width + '%');
30313             i.topEl.el.setStyle('width', width + '%');
30314             i.bottomEl.el.setStyle('width', width + '%');
30315         }, this);
30316         
30317     }
30318     
30319 });
30320 /*
30321  * - LGPL
30322  *
30323  * Nav Progress Item
30324  * 
30325  */
30326
30327 /**
30328  * @class Roo.bootstrap.NavProgressItem
30329  * @extends Roo.bootstrap.Component
30330  * Bootstrap NavProgressItem class
30331  * @cfg {String} rid the reference id
30332  * @cfg {Boolean} active (true|false) Is item active default false
30333  * @cfg {Boolean} disabled (true|false) Is item active default false
30334  * @cfg {String} html
30335  * @cfg {String} position (top|bottom) text position default bottom
30336  * @cfg {String} icon show icon instead of number
30337  * 
30338  * @constructor
30339  * Create a new NavProgressItem
30340  * @param {Object} config The config object
30341  */
30342 Roo.bootstrap.NavProgressItem = function(config){
30343     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30344     this.addEvents({
30345         // raw events
30346         /**
30347          * @event click
30348          * The raw click event for the entire grid.
30349          * @param {Roo.bootstrap.NavProgressItem} this
30350          * @param {Roo.EventObject} e
30351          */
30352         "click" : true
30353     });
30354    
30355 };
30356
30357 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30358     
30359     rid : '',
30360     active : false,
30361     disabled : false,
30362     html : '',
30363     position : 'bottom',
30364     icon : false,
30365     
30366     getAutoCreate : function()
30367     {
30368         var iconCls = 'roo-navigation-bar-item-icon';
30369         
30370         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30371         
30372         var cfg = {
30373             tag: 'li',
30374             cls: 'roo-navigation-bar-item',
30375             cn : [
30376                 {
30377                     tag : 'i',
30378                     cls : iconCls
30379                 }
30380             ]
30381         };
30382         
30383         if(this.active){
30384             cfg.cls += ' active';
30385         }
30386         if(this.disabled){
30387             cfg.cls += ' disabled';
30388         }
30389         
30390         return cfg;
30391     },
30392     
30393     disable : function()
30394     {
30395         this.setDisabled(true);
30396     },
30397     
30398     enable : function()
30399     {
30400         this.setDisabled(false);
30401     },
30402     
30403     initEvents: function() 
30404     {
30405         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30406         
30407         this.iconEl.on('click', this.onClick, this);
30408     },
30409     
30410     onClick : function(e)
30411     {
30412         e.preventDefault();
30413         
30414         if(this.disabled){
30415             return;
30416         }
30417         
30418         if(this.fireEvent('click', this, e) === false){
30419             return;
30420         };
30421         
30422         this.parent().setActiveItem(this);
30423     },
30424     
30425     isActive: function () 
30426     {
30427         return this.active;
30428     },
30429     
30430     setActive : function(state)
30431     {
30432         if(this.active == state){
30433             return;
30434         }
30435         
30436         this.active = state;
30437         
30438         if (state) {
30439             this.el.addClass('active');
30440             return;
30441         }
30442         
30443         this.el.removeClass('active');
30444         
30445         return;
30446     },
30447     
30448     setDisabled : function(state)
30449     {
30450         if(this.disabled == state){
30451             return;
30452         }
30453         
30454         this.disabled = state;
30455         
30456         if (state) {
30457             this.el.addClass('disabled');
30458             return;
30459         }
30460         
30461         this.el.removeClass('disabled');
30462     },
30463     
30464     tooltipEl : function()
30465     {
30466         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30467     }
30468 });
30469  
30470
30471  /*
30472  * - LGPL
30473  *
30474  * FieldLabel
30475  * 
30476  */
30477
30478 /**
30479  * @class Roo.bootstrap.FieldLabel
30480  * @extends Roo.bootstrap.Component
30481  * Bootstrap FieldLabel class
30482  * @cfg {String} html contents of the element
30483  * @cfg {String} tag tag of the element default label
30484  * @cfg {String} cls class of the element
30485  * @cfg {String} target label target 
30486  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30487  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30488  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30489  * @cfg {String} iconTooltip default "This field is required"
30490  * @cfg {String} indicatorpos (left|right) default left
30491  * 
30492  * @constructor
30493  * Create a new FieldLabel
30494  * @param {Object} config The config object
30495  */
30496
30497 Roo.bootstrap.FieldLabel = function(config){
30498     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30499     
30500     this.addEvents({
30501             /**
30502              * @event invalid
30503              * Fires after the field has been marked as invalid.
30504              * @param {Roo.form.FieldLabel} this
30505              * @param {String} msg The validation message
30506              */
30507             invalid : true,
30508             /**
30509              * @event valid
30510              * Fires after the field has been validated with no errors.
30511              * @param {Roo.form.FieldLabel} this
30512              */
30513             valid : true
30514         });
30515 };
30516
30517 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30518     
30519     tag: 'label',
30520     cls: '',
30521     html: '',
30522     target: '',
30523     allowBlank : true,
30524     invalidClass : 'has-warning',
30525     validClass : 'has-success',
30526     iconTooltip : 'This field is required',
30527     indicatorpos : 'left',
30528     
30529     getAutoCreate : function(){
30530         
30531         var cls = "";
30532         if (!this.allowBlank) {
30533             cls  = "visible";
30534         }
30535         
30536         var cfg = {
30537             tag : this.tag,
30538             cls : 'roo-bootstrap-field-label ' + this.cls,
30539             for : this.target,
30540             cn : [
30541                 {
30542                     tag : 'i',
30543                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30544                     tooltip : this.iconTooltip
30545                 },
30546                 {
30547                     tag : 'span',
30548                     html : this.html
30549                 }
30550             ] 
30551         };
30552         
30553         if(this.indicatorpos == 'right'){
30554             var cfg = {
30555                 tag : this.tag,
30556                 cls : 'roo-bootstrap-field-label ' + this.cls,
30557                 for : this.target,
30558                 cn : [
30559                     {
30560                         tag : 'span',
30561                         html : this.html
30562                     },
30563                     {
30564                         tag : 'i',
30565                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30566                         tooltip : this.iconTooltip
30567                     }
30568                 ] 
30569             };
30570         }
30571         
30572         return cfg;
30573     },
30574     
30575     initEvents: function() 
30576     {
30577         Roo.bootstrap.Element.superclass.initEvents.call(this);
30578         
30579         this.indicator = this.indicatorEl();
30580         
30581         if(this.indicator){
30582             this.indicator.removeClass('visible');
30583             this.indicator.addClass('invisible');
30584         }
30585         
30586         Roo.bootstrap.FieldLabel.register(this);
30587     },
30588     
30589     indicatorEl : function()
30590     {
30591         var indicator = this.el.select('i.roo-required-indicator',true).first();
30592         
30593         if(!indicator){
30594             return false;
30595         }
30596         
30597         return indicator;
30598         
30599     },
30600     
30601     /**
30602      * Mark this field as valid
30603      */
30604     markValid : function()
30605     {
30606         if(this.indicator){
30607             this.indicator.removeClass('visible');
30608             this.indicator.addClass('invisible');
30609         }
30610         if (Roo.bootstrap.version == 3) {
30611             this.el.removeClass(this.invalidClass);
30612             this.el.addClass(this.validClass);
30613         } else {
30614             this.el.removeClass('is-invalid');
30615             this.el.addClass('is-valid');
30616         }
30617         
30618         
30619         this.fireEvent('valid', this);
30620     },
30621     
30622     /**
30623      * Mark this field as invalid
30624      * @param {String} msg The validation message
30625      */
30626     markInvalid : function(msg)
30627     {
30628         if(this.indicator){
30629             this.indicator.removeClass('invisible');
30630             this.indicator.addClass('visible');
30631         }
30632           if (Roo.bootstrap.version == 3) {
30633             this.el.removeClass(this.validClass);
30634             this.el.addClass(this.invalidClass);
30635         } else {
30636             this.el.removeClass('is-valid');
30637             this.el.addClass('is-invalid');
30638         }
30639         
30640         
30641         this.fireEvent('invalid', this, msg);
30642     }
30643     
30644    
30645 });
30646
30647 Roo.apply(Roo.bootstrap.FieldLabel, {
30648     
30649     groups: {},
30650     
30651      /**
30652     * register a FieldLabel Group
30653     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30654     */
30655     register : function(label)
30656     {
30657         if(this.groups.hasOwnProperty(label.target)){
30658             return;
30659         }
30660      
30661         this.groups[label.target] = label;
30662         
30663     },
30664     /**
30665     * fetch a FieldLabel Group based on the target
30666     * @param {string} target
30667     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30668     */
30669     get: function(target) {
30670         if (typeof(this.groups[target]) == 'undefined') {
30671             return false;
30672         }
30673         
30674         return this.groups[target] ;
30675     }
30676 });
30677
30678  
30679
30680  /*
30681  * - LGPL
30682  *
30683  * page DateSplitField.
30684  * 
30685  */
30686
30687
30688 /**
30689  * @class Roo.bootstrap.DateSplitField
30690  * @extends Roo.bootstrap.Component
30691  * Bootstrap DateSplitField class
30692  * @cfg {string} fieldLabel - the label associated
30693  * @cfg {Number} labelWidth set the width of label (0-12)
30694  * @cfg {String} labelAlign (top|left)
30695  * @cfg {Boolean} dayAllowBlank (true|false) default false
30696  * @cfg {Boolean} monthAllowBlank (true|false) default false
30697  * @cfg {Boolean} yearAllowBlank (true|false) default false
30698  * @cfg {string} dayPlaceholder 
30699  * @cfg {string} monthPlaceholder
30700  * @cfg {string} yearPlaceholder
30701  * @cfg {string} dayFormat default 'd'
30702  * @cfg {string} monthFormat default 'm'
30703  * @cfg {string} yearFormat default 'Y'
30704  * @cfg {Number} labellg set the width of label (1-12)
30705  * @cfg {Number} labelmd set the width of label (1-12)
30706  * @cfg {Number} labelsm set the width of label (1-12)
30707  * @cfg {Number} labelxs set the width of label (1-12)
30708
30709  *     
30710  * @constructor
30711  * Create a new DateSplitField
30712  * @param {Object} config The config object
30713  */
30714
30715 Roo.bootstrap.DateSplitField = function(config){
30716     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30717     
30718     this.addEvents({
30719         // raw events
30720          /**
30721          * @event years
30722          * getting the data of years
30723          * @param {Roo.bootstrap.DateSplitField} this
30724          * @param {Object} years
30725          */
30726         "years" : true,
30727         /**
30728          * @event days
30729          * getting the data of days
30730          * @param {Roo.bootstrap.DateSplitField} this
30731          * @param {Object} days
30732          */
30733         "days" : true,
30734         /**
30735          * @event invalid
30736          * Fires after the field has been marked as invalid.
30737          * @param {Roo.form.Field} this
30738          * @param {String} msg The validation message
30739          */
30740         invalid : true,
30741        /**
30742          * @event valid
30743          * Fires after the field has been validated with no errors.
30744          * @param {Roo.form.Field} this
30745          */
30746         valid : true
30747     });
30748 };
30749
30750 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30751     
30752     fieldLabel : '',
30753     labelAlign : 'top',
30754     labelWidth : 3,
30755     dayAllowBlank : false,
30756     monthAllowBlank : false,
30757     yearAllowBlank : false,
30758     dayPlaceholder : '',
30759     monthPlaceholder : '',
30760     yearPlaceholder : '',
30761     dayFormat : 'd',
30762     monthFormat : 'm',
30763     yearFormat : 'Y',
30764     isFormField : true,
30765     labellg : 0,
30766     labelmd : 0,
30767     labelsm : 0,
30768     labelxs : 0,
30769     
30770     getAutoCreate : function()
30771     {
30772         var cfg = {
30773             tag : 'div',
30774             cls : 'row roo-date-split-field-group',
30775             cn : [
30776                 {
30777                     tag : 'input',
30778                     type : 'hidden',
30779                     cls : 'form-hidden-field roo-date-split-field-group-value',
30780                     name : this.name
30781                 }
30782             ]
30783         };
30784         
30785         var labelCls = 'col-md-12';
30786         var contentCls = 'col-md-4';
30787         
30788         if(this.fieldLabel){
30789             
30790             var label = {
30791                 tag : 'div',
30792                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30793                 cn : [
30794                     {
30795                         tag : 'label',
30796                         html : this.fieldLabel
30797                     }
30798                 ]
30799             };
30800             
30801             if(this.labelAlign == 'left'){
30802             
30803                 if(this.labelWidth > 12){
30804                     label.style = "width: " + this.labelWidth + 'px';
30805                 }
30806
30807                 if(this.labelWidth < 13 && this.labelmd == 0){
30808                     this.labelmd = this.labelWidth;
30809                 }
30810
30811                 if(this.labellg > 0){
30812                     labelCls = ' col-lg-' + this.labellg;
30813                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30814                 }
30815
30816                 if(this.labelmd > 0){
30817                     labelCls = ' col-md-' + this.labelmd;
30818                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30819                 }
30820
30821                 if(this.labelsm > 0){
30822                     labelCls = ' col-sm-' + this.labelsm;
30823                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30824                 }
30825
30826                 if(this.labelxs > 0){
30827                     labelCls = ' col-xs-' + this.labelxs;
30828                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30829                 }
30830             }
30831             
30832             label.cls += ' ' + labelCls;
30833             
30834             cfg.cn.push(label);
30835         }
30836         
30837         Roo.each(['day', 'month', 'year'], function(t){
30838             cfg.cn.push({
30839                 tag : 'div',
30840                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30841             });
30842         }, this);
30843         
30844         return cfg;
30845     },
30846     
30847     inputEl: function ()
30848     {
30849         return this.el.select('.roo-date-split-field-group-value', true).first();
30850     },
30851     
30852     onRender : function(ct, position) 
30853     {
30854         var _this = this;
30855         
30856         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30857         
30858         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30859         
30860         this.dayField = new Roo.bootstrap.ComboBox({
30861             allowBlank : this.dayAllowBlank,
30862             alwaysQuery : true,
30863             displayField : 'value',
30864             editable : false,
30865             fieldLabel : '',
30866             forceSelection : true,
30867             mode : 'local',
30868             placeholder : this.dayPlaceholder,
30869             selectOnFocus : true,
30870             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30871             triggerAction : 'all',
30872             typeAhead : true,
30873             valueField : 'value',
30874             store : new Roo.data.SimpleStore({
30875                 data : (function() {    
30876                     var days = [];
30877                     _this.fireEvent('days', _this, days);
30878                     return days;
30879                 })(),
30880                 fields : [ 'value' ]
30881             }),
30882             listeners : {
30883                 select : function (_self, record, index)
30884                 {
30885                     _this.setValue(_this.getValue());
30886                 }
30887             }
30888         });
30889
30890         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30891         
30892         this.monthField = new Roo.bootstrap.MonthField({
30893             after : '<i class=\"fa fa-calendar\"></i>',
30894             allowBlank : this.monthAllowBlank,
30895             placeholder : this.monthPlaceholder,
30896             readOnly : true,
30897             listeners : {
30898                 render : function (_self)
30899                 {
30900                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30901                         e.preventDefault();
30902                         _self.focus();
30903                     });
30904                 },
30905                 select : function (_self, oldvalue, newvalue)
30906                 {
30907                     _this.setValue(_this.getValue());
30908                 }
30909             }
30910         });
30911         
30912         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30913         
30914         this.yearField = new Roo.bootstrap.ComboBox({
30915             allowBlank : this.yearAllowBlank,
30916             alwaysQuery : true,
30917             displayField : 'value',
30918             editable : false,
30919             fieldLabel : '',
30920             forceSelection : true,
30921             mode : 'local',
30922             placeholder : this.yearPlaceholder,
30923             selectOnFocus : true,
30924             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30925             triggerAction : 'all',
30926             typeAhead : true,
30927             valueField : 'value',
30928             store : new Roo.data.SimpleStore({
30929                 data : (function() {
30930                     var years = [];
30931                     _this.fireEvent('years', _this, years);
30932                     return years;
30933                 })(),
30934                 fields : [ 'value' ]
30935             }),
30936             listeners : {
30937                 select : function (_self, record, index)
30938                 {
30939                     _this.setValue(_this.getValue());
30940                 }
30941             }
30942         });
30943
30944         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30945     },
30946     
30947     setValue : function(v, format)
30948     {
30949         this.inputEl.dom.value = v;
30950         
30951         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30952         
30953         var d = Date.parseDate(v, f);
30954         
30955         if(!d){
30956             this.validate();
30957             return;
30958         }
30959         
30960         this.setDay(d.format(this.dayFormat));
30961         this.setMonth(d.format(this.monthFormat));
30962         this.setYear(d.format(this.yearFormat));
30963         
30964         this.validate();
30965         
30966         return;
30967     },
30968     
30969     setDay : function(v)
30970     {
30971         this.dayField.setValue(v);
30972         this.inputEl.dom.value = this.getValue();
30973         this.validate();
30974         return;
30975     },
30976     
30977     setMonth : function(v)
30978     {
30979         this.monthField.setValue(v, true);
30980         this.inputEl.dom.value = this.getValue();
30981         this.validate();
30982         return;
30983     },
30984     
30985     setYear : function(v)
30986     {
30987         this.yearField.setValue(v);
30988         this.inputEl.dom.value = this.getValue();
30989         this.validate();
30990         return;
30991     },
30992     
30993     getDay : function()
30994     {
30995         return this.dayField.getValue();
30996     },
30997     
30998     getMonth : function()
30999     {
31000         return this.monthField.getValue();
31001     },
31002     
31003     getYear : function()
31004     {
31005         return this.yearField.getValue();
31006     },
31007     
31008     getValue : function()
31009     {
31010         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31011         
31012         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31013         
31014         return date;
31015     },
31016     
31017     reset : function()
31018     {
31019         this.setDay('');
31020         this.setMonth('');
31021         this.setYear('');
31022         this.inputEl.dom.value = '';
31023         this.validate();
31024         return;
31025     },
31026     
31027     validate : function()
31028     {
31029         var d = this.dayField.validate();
31030         var m = this.monthField.validate();
31031         var y = this.yearField.validate();
31032         
31033         var valid = true;
31034         
31035         if(
31036                 (!this.dayAllowBlank && !d) ||
31037                 (!this.monthAllowBlank && !m) ||
31038                 (!this.yearAllowBlank && !y)
31039         ){
31040             valid = false;
31041         }
31042         
31043         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31044             return valid;
31045         }
31046         
31047         if(valid){
31048             this.markValid();
31049             return valid;
31050         }
31051         
31052         this.markInvalid();
31053         
31054         return valid;
31055     },
31056     
31057     markValid : function()
31058     {
31059         
31060         var label = this.el.select('label', true).first();
31061         var icon = this.el.select('i.fa-star', true).first();
31062
31063         if(label && icon){
31064             icon.remove();
31065         }
31066         
31067         this.fireEvent('valid', this);
31068     },
31069     
31070      /**
31071      * Mark this field as invalid
31072      * @param {String} msg The validation message
31073      */
31074     markInvalid : function(msg)
31075     {
31076         
31077         var label = this.el.select('label', true).first();
31078         var icon = this.el.select('i.fa-star', true).first();
31079
31080         if(label && !icon){
31081             this.el.select('.roo-date-split-field-label', true).createChild({
31082                 tag : 'i',
31083                 cls : 'text-danger fa fa-lg fa-star',
31084                 tooltip : 'This field is required',
31085                 style : 'margin-right:5px;'
31086             }, label, true);
31087         }
31088         
31089         this.fireEvent('invalid', this, msg);
31090     },
31091     
31092     clearInvalid : function()
31093     {
31094         var label = this.el.select('label', true).first();
31095         var icon = this.el.select('i.fa-star', true).first();
31096
31097         if(label && icon){
31098             icon.remove();
31099         }
31100         
31101         this.fireEvent('valid', this);
31102     },
31103     
31104     getName: function()
31105     {
31106         return this.name;
31107     }
31108     
31109 });
31110
31111  /**
31112  *
31113  * This is based on 
31114  * http://masonry.desandro.com
31115  *
31116  * The idea is to render all the bricks based on vertical width...
31117  *
31118  * The original code extends 'outlayer' - we might need to use that....
31119  * 
31120  */
31121
31122
31123 /**
31124  * @class Roo.bootstrap.LayoutMasonry
31125  * @extends Roo.bootstrap.Component
31126  * Bootstrap Layout Masonry class
31127  * 
31128  * @constructor
31129  * Create a new Element
31130  * @param {Object} config The config object
31131  */
31132
31133 Roo.bootstrap.LayoutMasonry = function(config){
31134     
31135     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31136     
31137     this.bricks = [];
31138     
31139     Roo.bootstrap.LayoutMasonry.register(this);
31140     
31141     this.addEvents({
31142         // raw events
31143         /**
31144          * @event layout
31145          * Fire after layout the items
31146          * @param {Roo.bootstrap.LayoutMasonry} this
31147          * @param {Roo.EventObject} e
31148          */
31149         "layout" : true
31150     });
31151     
31152 };
31153
31154 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31155     
31156     /**
31157      * @cfg {Boolean} isLayoutInstant = no animation?
31158      */   
31159     isLayoutInstant : false, // needed?
31160    
31161     /**
31162      * @cfg {Number} boxWidth  width of the columns
31163      */   
31164     boxWidth : 450,
31165     
31166       /**
31167      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31168      */   
31169     boxHeight : 0,
31170     
31171     /**
31172      * @cfg {Number} padWidth padding below box..
31173      */   
31174     padWidth : 10, 
31175     
31176     /**
31177      * @cfg {Number} gutter gutter width..
31178      */   
31179     gutter : 10,
31180     
31181      /**
31182      * @cfg {Number} maxCols maximum number of columns
31183      */   
31184     
31185     maxCols: 0,
31186     
31187     /**
31188      * @cfg {Boolean} isAutoInitial defalut true
31189      */   
31190     isAutoInitial : true, 
31191     
31192     containerWidth: 0,
31193     
31194     /**
31195      * @cfg {Boolean} isHorizontal defalut false
31196      */   
31197     isHorizontal : false, 
31198
31199     currentSize : null,
31200     
31201     tag: 'div',
31202     
31203     cls: '',
31204     
31205     bricks: null, //CompositeElement
31206     
31207     cols : 1,
31208     
31209     _isLayoutInited : false,
31210     
31211 //    isAlternative : false, // only use for vertical layout...
31212     
31213     /**
31214      * @cfg {Number} alternativePadWidth padding below box..
31215      */   
31216     alternativePadWidth : 50,
31217     
31218     selectedBrick : [],
31219     
31220     getAutoCreate : function(){
31221         
31222         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31223         
31224         var cfg = {
31225             tag: this.tag,
31226             cls: 'blog-masonary-wrapper ' + this.cls,
31227             cn : {
31228                 cls : 'mas-boxes masonary'
31229             }
31230         };
31231         
31232         return cfg;
31233     },
31234     
31235     getChildContainer: function( )
31236     {
31237         if (this.boxesEl) {
31238             return this.boxesEl;
31239         }
31240         
31241         this.boxesEl = this.el.select('.mas-boxes').first();
31242         
31243         return this.boxesEl;
31244     },
31245     
31246     
31247     initEvents : function()
31248     {
31249         var _this = this;
31250         
31251         if(this.isAutoInitial){
31252             Roo.log('hook children rendered');
31253             this.on('childrenrendered', function() {
31254                 Roo.log('children rendered');
31255                 _this.initial();
31256             } ,this);
31257         }
31258     },
31259     
31260     initial : function()
31261     {
31262         this.selectedBrick = [];
31263         
31264         this.currentSize = this.el.getBox(true);
31265         
31266         Roo.EventManager.onWindowResize(this.resize, this); 
31267
31268         if(!this.isAutoInitial){
31269             this.layout();
31270             return;
31271         }
31272         
31273         this.layout();
31274         
31275         return;
31276         //this.layout.defer(500,this);
31277         
31278     },
31279     
31280     resize : function()
31281     {
31282         var cs = this.el.getBox(true);
31283         
31284         if (
31285                 this.currentSize.width == cs.width && 
31286                 this.currentSize.x == cs.x && 
31287                 this.currentSize.height == cs.height && 
31288                 this.currentSize.y == cs.y 
31289         ) {
31290             Roo.log("no change in with or X or Y");
31291             return;
31292         }
31293         
31294         this.currentSize = cs;
31295         
31296         this.layout();
31297         
31298     },
31299     
31300     layout : function()
31301     {   
31302         this._resetLayout();
31303         
31304         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31305         
31306         this.layoutItems( isInstant );
31307       
31308         this._isLayoutInited = true;
31309         
31310         this.fireEvent('layout', this);
31311         
31312     },
31313     
31314     _resetLayout : function()
31315     {
31316         if(this.isHorizontal){
31317             this.horizontalMeasureColumns();
31318             return;
31319         }
31320         
31321         this.verticalMeasureColumns();
31322         
31323     },
31324     
31325     verticalMeasureColumns : function()
31326     {
31327         this.getContainerWidth();
31328         
31329 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31330 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31331 //            return;
31332 //        }
31333         
31334         var boxWidth = this.boxWidth + this.padWidth;
31335         
31336         if(this.containerWidth < this.boxWidth){
31337             boxWidth = this.containerWidth
31338         }
31339         
31340         var containerWidth = this.containerWidth;
31341         
31342         var cols = Math.floor(containerWidth / boxWidth);
31343         
31344         this.cols = Math.max( cols, 1 );
31345         
31346         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31347         
31348         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31349         
31350         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31351         
31352         this.colWidth = boxWidth + avail - this.padWidth;
31353         
31354         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31355         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31356     },
31357     
31358     horizontalMeasureColumns : function()
31359     {
31360         this.getContainerWidth();
31361         
31362         var boxWidth = this.boxWidth;
31363         
31364         if(this.containerWidth < boxWidth){
31365             boxWidth = this.containerWidth;
31366         }
31367         
31368         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31369         
31370         this.el.setHeight(boxWidth);
31371         
31372     },
31373     
31374     getContainerWidth : function()
31375     {
31376         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31377     },
31378     
31379     layoutItems : function( isInstant )
31380     {
31381         Roo.log(this.bricks);
31382         
31383         var items = Roo.apply([], this.bricks);
31384         
31385         if(this.isHorizontal){
31386             this._horizontalLayoutItems( items , isInstant );
31387             return;
31388         }
31389         
31390 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31391 //            this._verticalAlternativeLayoutItems( items , isInstant );
31392 //            return;
31393 //        }
31394         
31395         this._verticalLayoutItems( items , isInstant );
31396         
31397     },
31398     
31399     _verticalLayoutItems : function ( items , isInstant)
31400     {
31401         if ( !items || !items.length ) {
31402             return;
31403         }
31404         
31405         var standard = [
31406             ['xs', 'xs', 'xs', 'tall'],
31407             ['xs', 'xs', 'tall'],
31408             ['xs', 'xs', 'sm'],
31409             ['xs', 'xs', 'xs'],
31410             ['xs', 'tall'],
31411             ['xs', 'sm'],
31412             ['xs', 'xs'],
31413             ['xs'],
31414             
31415             ['sm', 'xs', 'xs'],
31416             ['sm', 'xs'],
31417             ['sm'],
31418             
31419             ['tall', 'xs', 'xs', 'xs'],
31420             ['tall', 'xs', 'xs'],
31421             ['tall', 'xs'],
31422             ['tall']
31423             
31424         ];
31425         
31426         var queue = [];
31427         
31428         var boxes = [];
31429         
31430         var box = [];
31431         
31432         Roo.each(items, function(item, k){
31433             
31434             switch (item.size) {
31435                 // these layouts take up a full box,
31436                 case 'md' :
31437                 case 'md-left' :
31438                 case 'md-right' :
31439                 case 'wide' :
31440                     
31441                     if(box.length){
31442                         boxes.push(box);
31443                         box = [];
31444                     }
31445                     
31446                     boxes.push([item]);
31447                     
31448                     break;
31449                     
31450                 case 'xs' :
31451                 case 'sm' :
31452                 case 'tall' :
31453                     
31454                     box.push(item);
31455                     
31456                     break;
31457                 default :
31458                     break;
31459                     
31460             }
31461             
31462         }, this);
31463         
31464         if(box.length){
31465             boxes.push(box);
31466             box = [];
31467         }
31468         
31469         var filterPattern = function(box, length)
31470         {
31471             if(!box.length){
31472                 return;
31473             }
31474             
31475             var match = false;
31476             
31477             var pattern = box.slice(0, length);
31478             
31479             var format = [];
31480             
31481             Roo.each(pattern, function(i){
31482                 format.push(i.size);
31483             }, this);
31484             
31485             Roo.each(standard, function(s){
31486                 
31487                 if(String(s) != String(format)){
31488                     return;
31489                 }
31490                 
31491                 match = true;
31492                 return false;
31493                 
31494             }, this);
31495             
31496             if(!match && length == 1){
31497                 return;
31498             }
31499             
31500             if(!match){
31501                 filterPattern(box, length - 1);
31502                 return;
31503             }
31504                 
31505             queue.push(pattern);
31506
31507             box = box.slice(length, box.length);
31508
31509             filterPattern(box, 4);
31510
31511             return;
31512             
31513         }
31514         
31515         Roo.each(boxes, function(box, k){
31516             
31517             if(!box.length){
31518                 return;
31519             }
31520             
31521             if(box.length == 1){
31522                 queue.push(box);
31523                 return;
31524             }
31525             
31526             filterPattern(box, 4);
31527             
31528         }, this);
31529         
31530         this._processVerticalLayoutQueue( queue, isInstant );
31531         
31532     },
31533     
31534 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31535 //    {
31536 //        if ( !items || !items.length ) {
31537 //            return;
31538 //        }
31539 //
31540 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31541 //        
31542 //    },
31543     
31544     _horizontalLayoutItems : function ( items , isInstant)
31545     {
31546         if ( !items || !items.length || items.length < 3) {
31547             return;
31548         }
31549         
31550         items.reverse();
31551         
31552         var eItems = items.slice(0, 3);
31553         
31554         items = items.slice(3, items.length);
31555         
31556         var standard = [
31557             ['xs', 'xs', 'xs', 'wide'],
31558             ['xs', 'xs', 'wide'],
31559             ['xs', 'xs', 'sm'],
31560             ['xs', 'xs', 'xs'],
31561             ['xs', 'wide'],
31562             ['xs', 'sm'],
31563             ['xs', 'xs'],
31564             ['xs'],
31565             
31566             ['sm', 'xs', 'xs'],
31567             ['sm', 'xs'],
31568             ['sm'],
31569             
31570             ['wide', 'xs', 'xs', 'xs'],
31571             ['wide', 'xs', 'xs'],
31572             ['wide', 'xs'],
31573             ['wide'],
31574             
31575             ['wide-thin']
31576         ];
31577         
31578         var queue = [];
31579         
31580         var boxes = [];
31581         
31582         var box = [];
31583         
31584         Roo.each(items, function(item, k){
31585             
31586             switch (item.size) {
31587                 case 'md' :
31588                 case 'md-left' :
31589                 case 'md-right' :
31590                 case 'tall' :
31591                     
31592                     if(box.length){
31593                         boxes.push(box);
31594                         box = [];
31595                     }
31596                     
31597                     boxes.push([item]);
31598                     
31599                     break;
31600                     
31601                 case 'xs' :
31602                 case 'sm' :
31603                 case 'wide' :
31604                 case 'wide-thin' :
31605                     
31606                     box.push(item);
31607                     
31608                     break;
31609                 default :
31610                     break;
31611                     
31612             }
31613             
31614         }, this);
31615         
31616         if(box.length){
31617             boxes.push(box);
31618             box = [];
31619         }
31620         
31621         var filterPattern = function(box, length)
31622         {
31623             if(!box.length){
31624                 return;
31625             }
31626             
31627             var match = false;
31628             
31629             var pattern = box.slice(0, length);
31630             
31631             var format = [];
31632             
31633             Roo.each(pattern, function(i){
31634                 format.push(i.size);
31635             }, this);
31636             
31637             Roo.each(standard, function(s){
31638                 
31639                 if(String(s) != String(format)){
31640                     return;
31641                 }
31642                 
31643                 match = true;
31644                 return false;
31645                 
31646             }, this);
31647             
31648             if(!match && length == 1){
31649                 return;
31650             }
31651             
31652             if(!match){
31653                 filterPattern(box, length - 1);
31654                 return;
31655             }
31656                 
31657             queue.push(pattern);
31658
31659             box = box.slice(length, box.length);
31660
31661             filterPattern(box, 4);
31662
31663             return;
31664             
31665         }
31666         
31667         Roo.each(boxes, function(box, k){
31668             
31669             if(!box.length){
31670                 return;
31671             }
31672             
31673             if(box.length == 1){
31674                 queue.push(box);
31675                 return;
31676             }
31677             
31678             filterPattern(box, 4);
31679             
31680         }, this);
31681         
31682         
31683         var prune = [];
31684         
31685         var pos = this.el.getBox(true);
31686         
31687         var minX = pos.x;
31688         
31689         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31690         
31691         var hit_end = false;
31692         
31693         Roo.each(queue, function(box){
31694             
31695             if(hit_end){
31696                 
31697                 Roo.each(box, function(b){
31698                 
31699                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31700                     b.el.hide();
31701
31702                 }, this);
31703
31704                 return;
31705             }
31706             
31707             var mx = 0;
31708             
31709             Roo.each(box, function(b){
31710                 
31711                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31712                 b.el.show();
31713
31714                 mx = Math.max(mx, b.x);
31715                 
31716             }, this);
31717             
31718             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31719             
31720             if(maxX < minX){
31721                 
31722                 Roo.each(box, function(b){
31723                 
31724                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31725                     b.el.hide();
31726                     
31727                 }, this);
31728                 
31729                 hit_end = true;
31730                 
31731                 return;
31732             }
31733             
31734             prune.push(box);
31735             
31736         }, this);
31737         
31738         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31739     },
31740     
31741     /** Sets position of item in DOM
31742     * @param {Element} item
31743     * @param {Number} x - horizontal position
31744     * @param {Number} y - vertical position
31745     * @param {Boolean} isInstant - disables transitions
31746     */
31747     _processVerticalLayoutQueue : function( queue, isInstant )
31748     {
31749         var pos = this.el.getBox(true);
31750         var x = pos.x;
31751         var y = pos.y;
31752         var maxY = [];
31753         
31754         for (var i = 0; i < this.cols; i++){
31755             maxY[i] = pos.y;
31756         }
31757         
31758         Roo.each(queue, function(box, k){
31759             
31760             var col = k % this.cols;
31761             
31762             Roo.each(box, function(b,kk){
31763                 
31764                 b.el.position('absolute');
31765                 
31766                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31767                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31768                 
31769                 if(b.size == 'md-left' || b.size == 'md-right'){
31770                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31771                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31772                 }
31773                 
31774                 b.el.setWidth(width);
31775                 b.el.setHeight(height);
31776                 // iframe?
31777                 b.el.select('iframe',true).setSize(width,height);
31778                 
31779             }, this);
31780             
31781             for (var i = 0; i < this.cols; i++){
31782                 
31783                 if(maxY[i] < maxY[col]){
31784                     col = i;
31785                     continue;
31786                 }
31787                 
31788                 col = Math.min(col, i);
31789                 
31790             }
31791             
31792             x = pos.x + col * (this.colWidth + this.padWidth);
31793             
31794             y = maxY[col];
31795             
31796             var positions = [];
31797             
31798             switch (box.length){
31799                 case 1 :
31800                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31801                     break;
31802                 case 2 :
31803                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31804                     break;
31805                 case 3 :
31806                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31807                     break;
31808                 case 4 :
31809                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31810                     break;
31811                 default :
31812                     break;
31813             }
31814             
31815             Roo.each(box, function(b,kk){
31816                 
31817                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31818                 
31819                 var sz = b.el.getSize();
31820                 
31821                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31822                 
31823             }, this);
31824             
31825         }, this);
31826         
31827         var mY = 0;
31828         
31829         for (var i = 0; i < this.cols; i++){
31830             mY = Math.max(mY, maxY[i]);
31831         }
31832         
31833         this.el.setHeight(mY - pos.y);
31834         
31835     },
31836     
31837 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31838 //    {
31839 //        var pos = this.el.getBox(true);
31840 //        var x = pos.x;
31841 //        var y = pos.y;
31842 //        var maxX = pos.right;
31843 //        
31844 //        var maxHeight = 0;
31845 //        
31846 //        Roo.each(items, function(item, k){
31847 //            
31848 //            var c = k % 2;
31849 //            
31850 //            item.el.position('absolute');
31851 //                
31852 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31853 //
31854 //            item.el.setWidth(width);
31855 //
31856 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31857 //
31858 //            item.el.setHeight(height);
31859 //            
31860 //            if(c == 0){
31861 //                item.el.setXY([x, y], isInstant ? false : true);
31862 //            } else {
31863 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31864 //            }
31865 //            
31866 //            y = y + height + this.alternativePadWidth;
31867 //            
31868 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31869 //            
31870 //        }, this);
31871 //        
31872 //        this.el.setHeight(maxHeight);
31873 //        
31874 //    },
31875     
31876     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31877     {
31878         var pos = this.el.getBox(true);
31879         
31880         var minX = pos.x;
31881         var minY = pos.y;
31882         
31883         var maxX = pos.right;
31884         
31885         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31886         
31887         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31888         
31889         Roo.each(queue, function(box, k){
31890             
31891             Roo.each(box, function(b, kk){
31892                 
31893                 b.el.position('absolute');
31894                 
31895                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31896                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31897                 
31898                 if(b.size == 'md-left' || b.size == 'md-right'){
31899                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31900                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31901                 }
31902                 
31903                 b.el.setWidth(width);
31904                 b.el.setHeight(height);
31905                 
31906             }, this);
31907             
31908             if(!box.length){
31909                 return;
31910             }
31911             
31912             var positions = [];
31913             
31914             switch (box.length){
31915                 case 1 :
31916                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31917                     break;
31918                 case 2 :
31919                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31920                     break;
31921                 case 3 :
31922                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31923                     break;
31924                 case 4 :
31925                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31926                     break;
31927                 default :
31928                     break;
31929             }
31930             
31931             Roo.each(box, function(b,kk){
31932                 
31933                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31934                 
31935                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31936                 
31937             }, this);
31938             
31939         }, this);
31940         
31941     },
31942     
31943     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31944     {
31945         Roo.each(eItems, function(b,k){
31946             
31947             b.size = (k == 0) ? 'sm' : 'xs';
31948             b.x = (k == 0) ? 2 : 1;
31949             b.y = (k == 0) ? 2 : 1;
31950             
31951             b.el.position('absolute');
31952             
31953             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31954                 
31955             b.el.setWidth(width);
31956             
31957             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31958             
31959             b.el.setHeight(height);
31960             
31961         }, this);
31962
31963         var positions = [];
31964         
31965         positions.push({
31966             x : maxX - this.unitWidth * 2 - this.gutter,
31967             y : minY
31968         });
31969         
31970         positions.push({
31971             x : maxX - this.unitWidth,
31972             y : minY + (this.unitWidth + this.gutter) * 2
31973         });
31974         
31975         positions.push({
31976             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31977             y : minY
31978         });
31979         
31980         Roo.each(eItems, function(b,k){
31981             
31982             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31983
31984         }, this);
31985         
31986     },
31987     
31988     getVerticalOneBoxColPositions : function(x, y, box)
31989     {
31990         var pos = [];
31991         
31992         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31993         
31994         if(box[0].size == 'md-left'){
31995             rand = 0;
31996         }
31997         
31998         if(box[0].size == 'md-right'){
31999             rand = 1;
32000         }
32001         
32002         pos.push({
32003             x : x + (this.unitWidth + this.gutter) * rand,
32004             y : y
32005         });
32006         
32007         return pos;
32008     },
32009     
32010     getVerticalTwoBoxColPositions : function(x, y, box)
32011     {
32012         var pos = [];
32013         
32014         if(box[0].size == 'xs'){
32015             
32016             pos.push({
32017                 x : x,
32018                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32019             });
32020
32021             pos.push({
32022                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32023                 y : y
32024             });
32025             
32026             return pos;
32027             
32028         }
32029         
32030         pos.push({
32031             x : x,
32032             y : y
32033         });
32034
32035         pos.push({
32036             x : x + (this.unitWidth + this.gutter) * 2,
32037             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32038         });
32039         
32040         return pos;
32041         
32042     },
32043     
32044     getVerticalThreeBoxColPositions : function(x, y, box)
32045     {
32046         var pos = [];
32047         
32048         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32049             
32050             pos.push({
32051                 x : x,
32052                 y : y
32053             });
32054
32055             pos.push({
32056                 x : x + (this.unitWidth + this.gutter) * 1,
32057                 y : y
32058             });
32059             
32060             pos.push({
32061                 x : x + (this.unitWidth + this.gutter) * 2,
32062                 y : y
32063             });
32064             
32065             return pos;
32066             
32067         }
32068         
32069         if(box[0].size == 'xs' && box[1].size == 'xs'){
32070             
32071             pos.push({
32072                 x : x,
32073                 y : y
32074             });
32075
32076             pos.push({
32077                 x : x,
32078                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32079             });
32080             
32081             pos.push({
32082                 x : x + (this.unitWidth + this.gutter) * 1,
32083                 y : y
32084             });
32085             
32086             return pos;
32087             
32088         }
32089         
32090         pos.push({
32091             x : x,
32092             y : y
32093         });
32094
32095         pos.push({
32096             x : x + (this.unitWidth + this.gutter) * 2,
32097             y : y
32098         });
32099
32100         pos.push({
32101             x : x + (this.unitWidth + this.gutter) * 2,
32102             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32103         });
32104             
32105         return pos;
32106         
32107     },
32108     
32109     getVerticalFourBoxColPositions : function(x, y, box)
32110     {
32111         var pos = [];
32112         
32113         if(box[0].size == 'xs'){
32114             
32115             pos.push({
32116                 x : x,
32117                 y : y
32118             });
32119
32120             pos.push({
32121                 x : x,
32122                 y : y + (this.unitHeight + this.gutter) * 1
32123             });
32124             
32125             pos.push({
32126                 x : x,
32127                 y : y + (this.unitHeight + this.gutter) * 2
32128             });
32129             
32130             pos.push({
32131                 x : x + (this.unitWidth + this.gutter) * 1,
32132                 y : y
32133             });
32134             
32135             return pos;
32136             
32137         }
32138         
32139         pos.push({
32140             x : x,
32141             y : y
32142         });
32143
32144         pos.push({
32145             x : x + (this.unitWidth + this.gutter) * 2,
32146             y : y
32147         });
32148
32149         pos.push({
32150             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32151             y : y + (this.unitHeight + this.gutter) * 1
32152         });
32153
32154         pos.push({
32155             x : x + (this.unitWidth + this.gutter) * 2,
32156             y : y + (this.unitWidth + this.gutter) * 2
32157         });
32158
32159         return pos;
32160         
32161     },
32162     
32163     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32164     {
32165         var pos = [];
32166         
32167         if(box[0].size == 'md-left'){
32168             pos.push({
32169                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32170                 y : minY
32171             });
32172             
32173             return pos;
32174         }
32175         
32176         if(box[0].size == 'md-right'){
32177             pos.push({
32178                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32179                 y : minY + (this.unitWidth + this.gutter) * 1
32180             });
32181             
32182             return pos;
32183         }
32184         
32185         var rand = Math.floor(Math.random() * (4 - box[0].y));
32186         
32187         pos.push({
32188             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32189             y : minY + (this.unitWidth + this.gutter) * rand
32190         });
32191         
32192         return pos;
32193         
32194     },
32195     
32196     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32197     {
32198         var pos = [];
32199         
32200         if(box[0].size == 'xs'){
32201             
32202             pos.push({
32203                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32204                 y : minY
32205             });
32206
32207             pos.push({
32208                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32209                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32210             });
32211             
32212             return pos;
32213             
32214         }
32215         
32216         pos.push({
32217             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32218             y : minY
32219         });
32220
32221         pos.push({
32222             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32223             y : minY + (this.unitWidth + this.gutter) * 2
32224         });
32225         
32226         return pos;
32227         
32228     },
32229     
32230     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32231     {
32232         var pos = [];
32233         
32234         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32235             
32236             pos.push({
32237                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32238                 y : minY
32239             });
32240
32241             pos.push({
32242                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32243                 y : minY + (this.unitWidth + this.gutter) * 1
32244             });
32245             
32246             pos.push({
32247                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32248                 y : minY + (this.unitWidth + this.gutter) * 2
32249             });
32250             
32251             return pos;
32252             
32253         }
32254         
32255         if(box[0].size == 'xs' && box[1].size == 'xs'){
32256             
32257             pos.push({
32258                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32259                 y : minY
32260             });
32261
32262             pos.push({
32263                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32264                 y : minY
32265             });
32266             
32267             pos.push({
32268                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32269                 y : minY + (this.unitWidth + this.gutter) * 1
32270             });
32271             
32272             return pos;
32273             
32274         }
32275         
32276         pos.push({
32277             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32278             y : minY
32279         });
32280
32281         pos.push({
32282             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32283             y : minY + (this.unitWidth + this.gutter) * 2
32284         });
32285
32286         pos.push({
32287             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32288             y : minY + (this.unitWidth + this.gutter) * 2
32289         });
32290             
32291         return pos;
32292         
32293     },
32294     
32295     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32296     {
32297         var pos = [];
32298         
32299         if(box[0].size == 'xs'){
32300             
32301             pos.push({
32302                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32303                 y : minY
32304             });
32305
32306             pos.push({
32307                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32308                 y : minY
32309             });
32310             
32311             pos.push({
32312                 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),
32313                 y : minY
32314             });
32315             
32316             pos.push({
32317                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32318                 y : minY + (this.unitWidth + this.gutter) * 1
32319             });
32320             
32321             return pos;
32322             
32323         }
32324         
32325         pos.push({
32326             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32327             y : minY
32328         });
32329         
32330         pos.push({
32331             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32332             y : minY + (this.unitWidth + this.gutter) * 2
32333         });
32334         
32335         pos.push({
32336             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32337             y : minY + (this.unitWidth + this.gutter) * 2
32338         });
32339         
32340         pos.push({
32341             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),
32342             y : minY + (this.unitWidth + this.gutter) * 2
32343         });
32344
32345         return pos;
32346         
32347     },
32348     
32349     /**
32350     * remove a Masonry Brick
32351     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32352     */
32353     removeBrick : function(brick_id)
32354     {
32355         if (!brick_id) {
32356             return;
32357         }
32358         
32359         for (var i = 0; i<this.bricks.length; i++) {
32360             if (this.bricks[i].id == brick_id) {
32361                 this.bricks.splice(i,1);
32362                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32363                 this.initial();
32364             }
32365         }
32366     },
32367     
32368     /**
32369     * adds a Masonry Brick
32370     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32371     */
32372     addBrick : function(cfg)
32373     {
32374         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32375         //this.register(cn);
32376         cn.parentId = this.id;
32377         cn.render(this.el);
32378         return cn;
32379     },
32380     
32381     /**
32382     * register a Masonry Brick
32383     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32384     */
32385     
32386     register : function(brick)
32387     {
32388         this.bricks.push(brick);
32389         brick.masonryId = this.id;
32390     },
32391     
32392     /**
32393     * clear all the Masonry Brick
32394     */
32395     clearAll : function()
32396     {
32397         this.bricks = [];
32398         //this.getChildContainer().dom.innerHTML = "";
32399         this.el.dom.innerHTML = '';
32400     },
32401     
32402     getSelected : function()
32403     {
32404         if (!this.selectedBrick) {
32405             return false;
32406         }
32407         
32408         return this.selectedBrick;
32409     }
32410 });
32411
32412 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32413     
32414     groups: {},
32415      /**
32416     * register a Masonry Layout
32417     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32418     */
32419     
32420     register : function(layout)
32421     {
32422         this.groups[layout.id] = layout;
32423     },
32424     /**
32425     * fetch a  Masonry Layout based on the masonry layout ID
32426     * @param {string} the masonry layout to add
32427     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32428     */
32429     
32430     get: function(layout_id) {
32431         if (typeof(this.groups[layout_id]) == 'undefined') {
32432             return false;
32433         }
32434         return this.groups[layout_id] ;
32435     }
32436     
32437     
32438     
32439 });
32440
32441  
32442
32443  /**
32444  *
32445  * This is based on 
32446  * http://masonry.desandro.com
32447  *
32448  * The idea is to render all the bricks based on vertical width...
32449  *
32450  * The original code extends 'outlayer' - we might need to use that....
32451  * 
32452  */
32453
32454
32455 /**
32456  * @class Roo.bootstrap.LayoutMasonryAuto
32457  * @extends Roo.bootstrap.Component
32458  * Bootstrap Layout Masonry class
32459  * 
32460  * @constructor
32461  * Create a new Element
32462  * @param {Object} config The config object
32463  */
32464
32465 Roo.bootstrap.LayoutMasonryAuto = function(config){
32466     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32467 };
32468
32469 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32470     
32471       /**
32472      * @cfg {Boolean} isFitWidth  - resize the width..
32473      */   
32474     isFitWidth : false,  // options..
32475     /**
32476      * @cfg {Boolean} isOriginLeft = left align?
32477      */   
32478     isOriginLeft : true,
32479     /**
32480      * @cfg {Boolean} isOriginTop = top align?
32481      */   
32482     isOriginTop : false,
32483     /**
32484      * @cfg {Boolean} isLayoutInstant = no animation?
32485      */   
32486     isLayoutInstant : false, // needed?
32487     /**
32488      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32489      */   
32490     isResizingContainer : true,
32491     /**
32492      * @cfg {Number} columnWidth  width of the columns 
32493      */   
32494     
32495     columnWidth : 0,
32496     
32497     /**
32498      * @cfg {Number} maxCols maximum number of columns
32499      */   
32500     
32501     maxCols: 0,
32502     /**
32503      * @cfg {Number} padHeight padding below box..
32504      */   
32505     
32506     padHeight : 10, 
32507     
32508     /**
32509      * @cfg {Boolean} isAutoInitial defalut true
32510      */   
32511     
32512     isAutoInitial : true, 
32513     
32514     // private?
32515     gutter : 0,
32516     
32517     containerWidth: 0,
32518     initialColumnWidth : 0,
32519     currentSize : null,
32520     
32521     colYs : null, // array.
32522     maxY : 0,
32523     padWidth: 10,
32524     
32525     
32526     tag: 'div',
32527     cls: '',
32528     bricks: null, //CompositeElement
32529     cols : 0, // array?
32530     // element : null, // wrapped now this.el
32531     _isLayoutInited : null, 
32532     
32533     
32534     getAutoCreate : function(){
32535         
32536         var cfg = {
32537             tag: this.tag,
32538             cls: 'blog-masonary-wrapper ' + this.cls,
32539             cn : {
32540                 cls : 'mas-boxes masonary'
32541             }
32542         };
32543         
32544         return cfg;
32545     },
32546     
32547     getChildContainer: function( )
32548     {
32549         if (this.boxesEl) {
32550             return this.boxesEl;
32551         }
32552         
32553         this.boxesEl = this.el.select('.mas-boxes').first();
32554         
32555         return this.boxesEl;
32556     },
32557     
32558     
32559     initEvents : function()
32560     {
32561         var _this = this;
32562         
32563         if(this.isAutoInitial){
32564             Roo.log('hook children rendered');
32565             this.on('childrenrendered', function() {
32566                 Roo.log('children rendered');
32567                 _this.initial();
32568             } ,this);
32569         }
32570         
32571     },
32572     
32573     initial : function()
32574     {
32575         this.reloadItems();
32576
32577         this.currentSize = this.el.getBox(true);
32578
32579         /// was window resize... - let's see if this works..
32580         Roo.EventManager.onWindowResize(this.resize, this); 
32581
32582         if(!this.isAutoInitial){
32583             this.layout();
32584             return;
32585         }
32586         
32587         this.layout.defer(500,this);
32588     },
32589     
32590     reloadItems: function()
32591     {
32592         this.bricks = this.el.select('.masonry-brick', true);
32593         
32594         this.bricks.each(function(b) {
32595             //Roo.log(b.getSize());
32596             if (!b.attr('originalwidth')) {
32597                 b.attr('originalwidth',  b.getSize().width);
32598             }
32599             
32600         });
32601         
32602         Roo.log(this.bricks.elements.length);
32603     },
32604     
32605     resize : function()
32606     {
32607         Roo.log('resize');
32608         var cs = this.el.getBox(true);
32609         
32610         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32611             Roo.log("no change in with or X");
32612             return;
32613         }
32614         this.currentSize = cs;
32615         this.layout();
32616     },
32617     
32618     layout : function()
32619     {
32620          Roo.log('layout');
32621         this._resetLayout();
32622         //this._manageStamps();
32623       
32624         // don't animate first layout
32625         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32626         this.layoutItems( isInstant );
32627       
32628         // flag for initalized
32629         this._isLayoutInited = true;
32630     },
32631     
32632     layoutItems : function( isInstant )
32633     {
32634         //var items = this._getItemsForLayout( this.items );
32635         // original code supports filtering layout items.. we just ignore it..
32636         
32637         this._layoutItems( this.bricks , isInstant );
32638       
32639         this._postLayout();
32640     },
32641     _layoutItems : function ( items , isInstant)
32642     {
32643        //this.fireEvent( 'layout', this, items );
32644     
32645
32646         if ( !items || !items.elements.length ) {
32647           // no items, emit event with empty array
32648             return;
32649         }
32650
32651         var queue = [];
32652         items.each(function(item) {
32653             Roo.log("layout item");
32654             Roo.log(item);
32655             // get x/y object from method
32656             var position = this._getItemLayoutPosition( item );
32657             // enqueue
32658             position.item = item;
32659             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32660             queue.push( position );
32661         }, this);
32662       
32663         this._processLayoutQueue( queue );
32664     },
32665     /** Sets position of item in DOM
32666     * @param {Element} item
32667     * @param {Number} x - horizontal position
32668     * @param {Number} y - vertical position
32669     * @param {Boolean} isInstant - disables transitions
32670     */
32671     _processLayoutQueue : function( queue )
32672     {
32673         for ( var i=0, len = queue.length; i < len; i++ ) {
32674             var obj = queue[i];
32675             obj.item.position('absolute');
32676             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32677         }
32678     },
32679       
32680     
32681     /**
32682     * Any logic you want to do after each layout,
32683     * i.e. size the container
32684     */
32685     _postLayout : function()
32686     {
32687         this.resizeContainer();
32688     },
32689     
32690     resizeContainer : function()
32691     {
32692         if ( !this.isResizingContainer ) {
32693             return;
32694         }
32695         var size = this._getContainerSize();
32696         if ( size ) {
32697             this.el.setSize(size.width,size.height);
32698             this.boxesEl.setSize(size.width,size.height);
32699         }
32700     },
32701     
32702     
32703     
32704     _resetLayout : function()
32705     {
32706         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32707         this.colWidth = this.el.getWidth();
32708         //this.gutter = this.el.getWidth(); 
32709         
32710         this.measureColumns();
32711
32712         // reset column Y
32713         var i = this.cols;
32714         this.colYs = [];
32715         while (i--) {
32716             this.colYs.push( 0 );
32717         }
32718     
32719         this.maxY = 0;
32720     },
32721
32722     measureColumns : function()
32723     {
32724         this.getContainerWidth();
32725       // if columnWidth is 0, default to outerWidth of first item
32726         if ( !this.columnWidth ) {
32727             var firstItem = this.bricks.first();
32728             Roo.log(firstItem);
32729             this.columnWidth  = this.containerWidth;
32730             if (firstItem && firstItem.attr('originalwidth') ) {
32731                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32732             }
32733             // columnWidth fall back to item of first element
32734             Roo.log("set column width?");
32735                         this.initialColumnWidth = this.columnWidth  ;
32736
32737             // if first elem has no width, default to size of container
32738             
32739         }
32740         
32741         
32742         if (this.initialColumnWidth) {
32743             this.columnWidth = this.initialColumnWidth;
32744         }
32745         
32746         
32747             
32748         // column width is fixed at the top - however if container width get's smaller we should
32749         // reduce it...
32750         
32751         // this bit calcs how man columns..
32752             
32753         var columnWidth = this.columnWidth += this.gutter;
32754       
32755         // calculate columns
32756         var containerWidth = this.containerWidth + this.gutter;
32757         
32758         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32759         // fix rounding errors, typically with gutters
32760         var excess = columnWidth - containerWidth % columnWidth;
32761         
32762         
32763         // if overshoot is less than a pixel, round up, otherwise floor it
32764         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32765         cols = Math[ mathMethod ]( cols );
32766         this.cols = Math.max( cols, 1 );
32767         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32768         
32769          // padding positioning..
32770         var totalColWidth = this.cols * this.columnWidth;
32771         var padavail = this.containerWidth - totalColWidth;
32772         // so for 2 columns - we need 3 'pads'
32773         
32774         var padNeeded = (1+this.cols) * this.padWidth;
32775         
32776         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32777         
32778         this.columnWidth += padExtra
32779         //this.padWidth = Math.floor(padavail /  ( this.cols));
32780         
32781         // adjust colum width so that padding is fixed??
32782         
32783         // we have 3 columns ... total = width * 3
32784         // we have X left over... that should be used by 
32785         
32786         //if (this.expandC) {
32787             
32788         //}
32789         
32790         
32791         
32792     },
32793     
32794     getContainerWidth : function()
32795     {
32796        /* // container is parent if fit width
32797         var container = this.isFitWidth ? this.element.parentNode : this.element;
32798         // check that this.size and size are there
32799         // IE8 triggers resize on body size change, so they might not be
32800         
32801         var size = getSize( container );  //FIXME
32802         this.containerWidth = size && size.innerWidth; //FIXME
32803         */
32804          
32805         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32806         
32807     },
32808     
32809     _getItemLayoutPosition : function( item )  // what is item?
32810     {
32811         // we resize the item to our columnWidth..
32812       
32813         item.setWidth(this.columnWidth);
32814         item.autoBoxAdjust  = false;
32815         
32816         var sz = item.getSize();
32817  
32818         // how many columns does this brick span
32819         var remainder = this.containerWidth % this.columnWidth;
32820         
32821         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32822         // round if off by 1 pixel, otherwise use ceil
32823         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32824         colSpan = Math.min( colSpan, this.cols );
32825         
32826         // normally this should be '1' as we dont' currently allow multi width columns..
32827         
32828         var colGroup = this._getColGroup( colSpan );
32829         // get the minimum Y value from the columns
32830         var minimumY = Math.min.apply( Math, colGroup );
32831         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32832         
32833         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32834          
32835         // position the brick
32836         var position = {
32837             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32838             y: this.currentSize.y + minimumY + this.padHeight
32839         };
32840         
32841         Roo.log(position);
32842         // apply setHeight to necessary columns
32843         var setHeight = minimumY + sz.height + this.padHeight;
32844         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32845         
32846         var setSpan = this.cols + 1 - colGroup.length;
32847         for ( var i = 0; i < setSpan; i++ ) {
32848           this.colYs[ shortColIndex + i ] = setHeight ;
32849         }
32850       
32851         return position;
32852     },
32853     
32854     /**
32855      * @param {Number} colSpan - number of columns the element spans
32856      * @returns {Array} colGroup
32857      */
32858     _getColGroup : function( colSpan )
32859     {
32860         if ( colSpan < 2 ) {
32861           // if brick spans only one column, use all the column Ys
32862           return this.colYs;
32863         }
32864       
32865         var colGroup = [];
32866         // how many different places could this brick fit horizontally
32867         var groupCount = this.cols + 1 - colSpan;
32868         // for each group potential horizontal position
32869         for ( var i = 0; i < groupCount; i++ ) {
32870           // make an array of colY values for that one group
32871           var groupColYs = this.colYs.slice( i, i + colSpan );
32872           // and get the max value of the array
32873           colGroup[i] = Math.max.apply( Math, groupColYs );
32874         }
32875         return colGroup;
32876     },
32877     /*
32878     _manageStamp : function( stamp )
32879     {
32880         var stampSize =  stamp.getSize();
32881         var offset = stamp.getBox();
32882         // get the columns that this stamp affects
32883         var firstX = this.isOriginLeft ? offset.x : offset.right;
32884         var lastX = firstX + stampSize.width;
32885         var firstCol = Math.floor( firstX / this.columnWidth );
32886         firstCol = Math.max( 0, firstCol );
32887         
32888         var lastCol = Math.floor( lastX / this.columnWidth );
32889         // lastCol should not go over if multiple of columnWidth #425
32890         lastCol -= lastX % this.columnWidth ? 0 : 1;
32891         lastCol = Math.min( this.cols - 1, lastCol );
32892         
32893         // set colYs to bottom of the stamp
32894         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32895             stampSize.height;
32896             
32897         for ( var i = firstCol; i <= lastCol; i++ ) {
32898           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32899         }
32900     },
32901     */
32902     
32903     _getContainerSize : function()
32904     {
32905         this.maxY = Math.max.apply( Math, this.colYs );
32906         var size = {
32907             height: this.maxY
32908         };
32909       
32910         if ( this.isFitWidth ) {
32911             size.width = this._getContainerFitWidth();
32912         }
32913       
32914         return size;
32915     },
32916     
32917     _getContainerFitWidth : function()
32918     {
32919         var unusedCols = 0;
32920         // count unused columns
32921         var i = this.cols;
32922         while ( --i ) {
32923           if ( this.colYs[i] !== 0 ) {
32924             break;
32925           }
32926           unusedCols++;
32927         }
32928         // fit container to columns that have been used
32929         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32930     },
32931     
32932     needsResizeLayout : function()
32933     {
32934         var previousWidth = this.containerWidth;
32935         this.getContainerWidth();
32936         return previousWidth !== this.containerWidth;
32937     }
32938  
32939 });
32940
32941  
32942
32943  /*
32944  * - LGPL
32945  *
32946  * element
32947  * 
32948  */
32949
32950 /**
32951  * @class Roo.bootstrap.MasonryBrick
32952  * @extends Roo.bootstrap.Component
32953  * Bootstrap MasonryBrick class
32954  * 
32955  * @constructor
32956  * Create a new MasonryBrick
32957  * @param {Object} config The config object
32958  */
32959
32960 Roo.bootstrap.MasonryBrick = function(config){
32961     
32962     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32963     
32964     Roo.bootstrap.MasonryBrick.register(this);
32965     
32966     this.addEvents({
32967         // raw events
32968         /**
32969          * @event click
32970          * When a MasonryBrick is clcik
32971          * @param {Roo.bootstrap.MasonryBrick} this
32972          * @param {Roo.EventObject} e
32973          */
32974         "click" : true
32975     });
32976 };
32977
32978 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32979     
32980     /**
32981      * @cfg {String} title
32982      */   
32983     title : '',
32984     /**
32985      * @cfg {String} html
32986      */   
32987     html : '',
32988     /**
32989      * @cfg {String} bgimage
32990      */   
32991     bgimage : '',
32992     /**
32993      * @cfg {String} videourl
32994      */   
32995     videourl : '',
32996     /**
32997      * @cfg {String} cls
32998      */   
32999     cls : '',
33000     /**
33001      * @cfg {String} href
33002      */   
33003     href : '',
33004     /**
33005      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33006      */   
33007     size : 'xs',
33008     
33009     /**
33010      * @cfg {String} placetitle (center|bottom)
33011      */   
33012     placetitle : '',
33013     
33014     /**
33015      * @cfg {Boolean} isFitContainer defalut true
33016      */   
33017     isFitContainer : true, 
33018     
33019     /**
33020      * @cfg {Boolean} preventDefault defalut false
33021      */   
33022     preventDefault : false, 
33023     
33024     /**
33025      * @cfg {Boolean} inverse defalut false
33026      */   
33027     maskInverse : false, 
33028     
33029     getAutoCreate : function()
33030     {
33031         if(!this.isFitContainer){
33032             return this.getSplitAutoCreate();
33033         }
33034         
33035         var cls = 'masonry-brick masonry-brick-full';
33036         
33037         if(this.href.length){
33038             cls += ' masonry-brick-link';
33039         }
33040         
33041         if(this.bgimage.length){
33042             cls += ' masonry-brick-image';
33043         }
33044         
33045         if(this.maskInverse){
33046             cls += ' mask-inverse';
33047         }
33048         
33049         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33050             cls += ' enable-mask';
33051         }
33052         
33053         if(this.size){
33054             cls += ' masonry-' + this.size + '-brick';
33055         }
33056         
33057         if(this.placetitle.length){
33058             
33059             switch (this.placetitle) {
33060                 case 'center' :
33061                     cls += ' masonry-center-title';
33062                     break;
33063                 case 'bottom' :
33064                     cls += ' masonry-bottom-title';
33065                     break;
33066                 default:
33067                     break;
33068             }
33069             
33070         } else {
33071             if(!this.html.length && !this.bgimage.length){
33072                 cls += ' masonry-center-title';
33073             }
33074
33075             if(!this.html.length && this.bgimage.length){
33076                 cls += ' masonry-bottom-title';
33077             }
33078         }
33079         
33080         if(this.cls){
33081             cls += ' ' + this.cls;
33082         }
33083         
33084         var cfg = {
33085             tag: (this.href.length) ? 'a' : 'div',
33086             cls: cls,
33087             cn: [
33088                 {
33089                     tag: 'div',
33090                     cls: 'masonry-brick-mask'
33091                 },
33092                 {
33093                     tag: 'div',
33094                     cls: 'masonry-brick-paragraph',
33095                     cn: []
33096                 }
33097             ]
33098         };
33099         
33100         if(this.href.length){
33101             cfg.href = this.href;
33102         }
33103         
33104         var cn = cfg.cn[1].cn;
33105         
33106         if(this.title.length){
33107             cn.push({
33108                 tag: 'h4',
33109                 cls: 'masonry-brick-title',
33110                 html: this.title
33111             });
33112         }
33113         
33114         if(this.html.length){
33115             cn.push({
33116                 tag: 'p',
33117                 cls: 'masonry-brick-text',
33118                 html: this.html
33119             });
33120         }
33121         
33122         if (!this.title.length && !this.html.length) {
33123             cfg.cn[1].cls += ' hide';
33124         }
33125         
33126         if(this.bgimage.length){
33127             cfg.cn.push({
33128                 tag: 'img',
33129                 cls: 'masonry-brick-image-view',
33130                 src: this.bgimage
33131             });
33132         }
33133         
33134         if(this.videourl.length){
33135             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33136             // youtube support only?
33137             cfg.cn.push({
33138                 tag: 'iframe',
33139                 cls: 'masonry-brick-image-view',
33140                 src: vurl,
33141                 frameborder : 0,
33142                 allowfullscreen : true
33143             });
33144         }
33145         
33146         return cfg;
33147         
33148     },
33149     
33150     getSplitAutoCreate : function()
33151     {
33152         var cls = 'masonry-brick masonry-brick-split';
33153         
33154         if(this.href.length){
33155             cls += ' masonry-brick-link';
33156         }
33157         
33158         if(this.bgimage.length){
33159             cls += ' masonry-brick-image';
33160         }
33161         
33162         if(this.size){
33163             cls += ' masonry-' + this.size + '-brick';
33164         }
33165         
33166         switch (this.placetitle) {
33167             case 'center' :
33168                 cls += ' masonry-center-title';
33169                 break;
33170             case 'bottom' :
33171                 cls += ' masonry-bottom-title';
33172                 break;
33173             default:
33174                 if(!this.bgimage.length){
33175                     cls += ' masonry-center-title';
33176                 }
33177
33178                 if(this.bgimage.length){
33179                     cls += ' masonry-bottom-title';
33180                 }
33181                 break;
33182         }
33183         
33184         if(this.cls){
33185             cls += ' ' + this.cls;
33186         }
33187         
33188         var cfg = {
33189             tag: (this.href.length) ? 'a' : 'div',
33190             cls: cls,
33191             cn: [
33192                 {
33193                     tag: 'div',
33194                     cls: 'masonry-brick-split-head',
33195                     cn: [
33196                         {
33197                             tag: 'div',
33198                             cls: 'masonry-brick-paragraph',
33199                             cn: []
33200                         }
33201                     ]
33202                 },
33203                 {
33204                     tag: 'div',
33205                     cls: 'masonry-brick-split-body',
33206                     cn: []
33207                 }
33208             ]
33209         };
33210         
33211         if(this.href.length){
33212             cfg.href = this.href;
33213         }
33214         
33215         if(this.title.length){
33216             cfg.cn[0].cn[0].cn.push({
33217                 tag: 'h4',
33218                 cls: 'masonry-brick-title',
33219                 html: this.title
33220             });
33221         }
33222         
33223         if(this.html.length){
33224             cfg.cn[1].cn.push({
33225                 tag: 'p',
33226                 cls: 'masonry-brick-text',
33227                 html: this.html
33228             });
33229         }
33230
33231         if(this.bgimage.length){
33232             cfg.cn[0].cn.push({
33233                 tag: 'img',
33234                 cls: 'masonry-brick-image-view',
33235                 src: this.bgimage
33236             });
33237         }
33238         
33239         if(this.videourl.length){
33240             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33241             // youtube support only?
33242             cfg.cn[0].cn.cn.push({
33243                 tag: 'iframe',
33244                 cls: 'masonry-brick-image-view',
33245                 src: vurl,
33246                 frameborder : 0,
33247                 allowfullscreen : true
33248             });
33249         }
33250         
33251         return cfg;
33252     },
33253     
33254     initEvents: function() 
33255     {
33256         switch (this.size) {
33257             case 'xs' :
33258                 this.x = 1;
33259                 this.y = 1;
33260                 break;
33261             case 'sm' :
33262                 this.x = 2;
33263                 this.y = 2;
33264                 break;
33265             case 'md' :
33266             case 'md-left' :
33267             case 'md-right' :
33268                 this.x = 3;
33269                 this.y = 3;
33270                 break;
33271             case 'tall' :
33272                 this.x = 2;
33273                 this.y = 3;
33274                 break;
33275             case 'wide' :
33276                 this.x = 3;
33277                 this.y = 2;
33278                 break;
33279             case 'wide-thin' :
33280                 this.x = 3;
33281                 this.y = 1;
33282                 break;
33283                         
33284             default :
33285                 break;
33286         }
33287         
33288         if(Roo.isTouch){
33289             this.el.on('touchstart', this.onTouchStart, this);
33290             this.el.on('touchmove', this.onTouchMove, this);
33291             this.el.on('touchend', this.onTouchEnd, this);
33292             this.el.on('contextmenu', this.onContextMenu, this);
33293         } else {
33294             this.el.on('mouseenter'  ,this.enter, this);
33295             this.el.on('mouseleave', this.leave, this);
33296             this.el.on('click', this.onClick, this);
33297         }
33298         
33299         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33300             this.parent().bricks.push(this);   
33301         }
33302         
33303     },
33304     
33305     onClick: function(e, el)
33306     {
33307         var time = this.endTimer - this.startTimer;
33308         // Roo.log(e.preventDefault());
33309         if(Roo.isTouch){
33310             if(time > 1000){
33311                 e.preventDefault();
33312                 return;
33313             }
33314         }
33315         
33316         if(!this.preventDefault){
33317             return;
33318         }
33319         
33320         e.preventDefault();
33321         
33322         if (this.activeClass != '') {
33323             this.selectBrick();
33324         }
33325         
33326         this.fireEvent('click', this, e);
33327     },
33328     
33329     enter: function(e, el)
33330     {
33331         e.preventDefault();
33332         
33333         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33334             return;
33335         }
33336         
33337         if(this.bgimage.length && this.html.length){
33338             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33339         }
33340     },
33341     
33342     leave: function(e, el)
33343     {
33344         e.preventDefault();
33345         
33346         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33347             return;
33348         }
33349         
33350         if(this.bgimage.length && this.html.length){
33351             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33352         }
33353     },
33354     
33355     onTouchStart: function(e, el)
33356     {
33357 //        e.preventDefault();
33358         
33359         this.touchmoved = false;
33360         
33361         if(!this.isFitContainer){
33362             return;
33363         }
33364         
33365         if(!this.bgimage.length || !this.html.length){
33366             return;
33367         }
33368         
33369         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33370         
33371         this.timer = new Date().getTime();
33372         
33373     },
33374     
33375     onTouchMove: function(e, el)
33376     {
33377         this.touchmoved = true;
33378     },
33379     
33380     onContextMenu : function(e,el)
33381     {
33382         e.preventDefault();
33383         e.stopPropagation();
33384         return false;
33385     },
33386     
33387     onTouchEnd: function(e, el)
33388     {
33389 //        e.preventDefault();
33390         
33391         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33392         
33393             this.leave(e,el);
33394             
33395             return;
33396         }
33397         
33398         if(!this.bgimage.length || !this.html.length){
33399             
33400             if(this.href.length){
33401                 window.location.href = this.href;
33402             }
33403             
33404             return;
33405         }
33406         
33407         if(!this.isFitContainer){
33408             return;
33409         }
33410         
33411         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33412         
33413         window.location.href = this.href;
33414     },
33415     
33416     //selection on single brick only
33417     selectBrick : function() {
33418         
33419         if (!this.parentId) {
33420             return;
33421         }
33422         
33423         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33424         var index = m.selectedBrick.indexOf(this.id);
33425         
33426         if ( index > -1) {
33427             m.selectedBrick.splice(index,1);
33428             this.el.removeClass(this.activeClass);
33429             return;
33430         }
33431         
33432         for(var i = 0; i < m.selectedBrick.length; i++) {
33433             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33434             b.el.removeClass(b.activeClass);
33435         }
33436         
33437         m.selectedBrick = [];
33438         
33439         m.selectedBrick.push(this.id);
33440         this.el.addClass(this.activeClass);
33441         return;
33442     },
33443     
33444     isSelected : function(){
33445         return this.el.hasClass(this.activeClass);
33446         
33447     }
33448 });
33449
33450 Roo.apply(Roo.bootstrap.MasonryBrick, {
33451     
33452     //groups: {},
33453     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33454      /**
33455     * register a Masonry Brick
33456     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33457     */
33458     
33459     register : function(brick)
33460     {
33461         //this.groups[brick.id] = brick;
33462         this.groups.add(brick.id, brick);
33463     },
33464     /**
33465     * fetch a  masonry brick based on the masonry brick ID
33466     * @param {string} the masonry brick to add
33467     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33468     */
33469     
33470     get: function(brick_id) 
33471     {
33472         // if (typeof(this.groups[brick_id]) == 'undefined') {
33473         //     return false;
33474         // }
33475         // return this.groups[brick_id] ;
33476         
33477         if(this.groups.key(brick_id)) {
33478             return this.groups.key(brick_id);
33479         }
33480         
33481         return false;
33482     }
33483     
33484     
33485     
33486 });
33487
33488  /*
33489  * - LGPL
33490  *
33491  * element
33492  * 
33493  */
33494
33495 /**
33496  * @class Roo.bootstrap.Brick
33497  * @extends Roo.bootstrap.Component
33498  * Bootstrap Brick class
33499  * 
33500  * @constructor
33501  * Create a new Brick
33502  * @param {Object} config The config object
33503  */
33504
33505 Roo.bootstrap.Brick = function(config){
33506     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33507     
33508     this.addEvents({
33509         // raw events
33510         /**
33511          * @event click
33512          * When a Brick is click
33513          * @param {Roo.bootstrap.Brick} this
33514          * @param {Roo.EventObject} e
33515          */
33516         "click" : true
33517     });
33518 };
33519
33520 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33521     
33522     /**
33523      * @cfg {String} title
33524      */   
33525     title : '',
33526     /**
33527      * @cfg {String} html
33528      */   
33529     html : '',
33530     /**
33531      * @cfg {String} bgimage
33532      */   
33533     bgimage : '',
33534     /**
33535      * @cfg {String} cls
33536      */   
33537     cls : '',
33538     /**
33539      * @cfg {String} href
33540      */   
33541     href : '',
33542     /**
33543      * @cfg {String} video
33544      */   
33545     video : '',
33546     /**
33547      * @cfg {Boolean} square
33548      */   
33549     square : true,
33550     
33551     getAutoCreate : function()
33552     {
33553         var cls = 'roo-brick';
33554         
33555         if(this.href.length){
33556             cls += ' roo-brick-link';
33557         }
33558         
33559         if(this.bgimage.length){
33560             cls += ' roo-brick-image';
33561         }
33562         
33563         if(!this.html.length && !this.bgimage.length){
33564             cls += ' roo-brick-center-title';
33565         }
33566         
33567         if(!this.html.length && this.bgimage.length){
33568             cls += ' roo-brick-bottom-title';
33569         }
33570         
33571         if(this.cls){
33572             cls += ' ' + this.cls;
33573         }
33574         
33575         var cfg = {
33576             tag: (this.href.length) ? 'a' : 'div',
33577             cls: cls,
33578             cn: [
33579                 {
33580                     tag: 'div',
33581                     cls: 'roo-brick-paragraph',
33582                     cn: []
33583                 }
33584             ]
33585         };
33586         
33587         if(this.href.length){
33588             cfg.href = this.href;
33589         }
33590         
33591         var cn = cfg.cn[0].cn;
33592         
33593         if(this.title.length){
33594             cn.push({
33595                 tag: 'h4',
33596                 cls: 'roo-brick-title',
33597                 html: this.title
33598             });
33599         }
33600         
33601         if(this.html.length){
33602             cn.push({
33603                 tag: 'p',
33604                 cls: 'roo-brick-text',
33605                 html: this.html
33606             });
33607         } else {
33608             cn.cls += ' hide';
33609         }
33610         
33611         if(this.bgimage.length){
33612             cfg.cn.push({
33613                 tag: 'img',
33614                 cls: 'roo-brick-image-view',
33615                 src: this.bgimage
33616             });
33617         }
33618         
33619         return cfg;
33620     },
33621     
33622     initEvents: function() 
33623     {
33624         if(this.title.length || this.html.length){
33625             this.el.on('mouseenter'  ,this.enter, this);
33626             this.el.on('mouseleave', this.leave, this);
33627         }
33628         
33629         Roo.EventManager.onWindowResize(this.resize, this); 
33630         
33631         if(this.bgimage.length){
33632             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33633             this.imageEl.on('load', this.onImageLoad, this);
33634             return;
33635         }
33636         
33637         this.resize();
33638     },
33639     
33640     onImageLoad : function()
33641     {
33642         this.resize();
33643     },
33644     
33645     resize : function()
33646     {
33647         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33648         
33649         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33650         
33651         if(this.bgimage.length){
33652             var image = this.el.select('.roo-brick-image-view', true).first();
33653             
33654             image.setWidth(paragraph.getWidth());
33655             
33656             if(this.square){
33657                 image.setHeight(paragraph.getWidth());
33658             }
33659             
33660             this.el.setHeight(image.getHeight());
33661             paragraph.setHeight(image.getHeight());
33662             
33663         }
33664         
33665     },
33666     
33667     enter: function(e, el)
33668     {
33669         e.preventDefault();
33670         
33671         if(this.bgimage.length){
33672             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33673             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33674         }
33675     },
33676     
33677     leave: function(e, el)
33678     {
33679         e.preventDefault();
33680         
33681         if(this.bgimage.length){
33682             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33683             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33684         }
33685     }
33686     
33687 });
33688
33689  
33690
33691  /*
33692  * - LGPL
33693  *
33694  * Number field 
33695  */
33696
33697 /**
33698  * @class Roo.bootstrap.NumberField
33699  * @extends Roo.bootstrap.Input
33700  * Bootstrap NumberField class
33701  * 
33702  * 
33703  * 
33704  * 
33705  * @constructor
33706  * Create a new NumberField
33707  * @param {Object} config The config object
33708  */
33709
33710 Roo.bootstrap.NumberField = function(config){
33711     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33712 };
33713
33714 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33715     
33716     /**
33717      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33718      */
33719     allowDecimals : true,
33720     /**
33721      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33722      */
33723     decimalSeparator : ".",
33724     /**
33725      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33726      */
33727     decimalPrecision : 2,
33728     /**
33729      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33730      */
33731     allowNegative : true,
33732     
33733     /**
33734      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33735      */
33736     allowZero: true,
33737     /**
33738      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33739      */
33740     minValue : Number.NEGATIVE_INFINITY,
33741     /**
33742      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33743      */
33744     maxValue : Number.MAX_VALUE,
33745     /**
33746      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33747      */
33748     minText : "The minimum value for this field is {0}",
33749     /**
33750      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33751      */
33752     maxText : "The maximum value for this field is {0}",
33753     /**
33754      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33755      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33756      */
33757     nanText : "{0} is not a valid number",
33758     /**
33759      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33760      */
33761     thousandsDelimiter : false,
33762     /**
33763      * @cfg {String} valueAlign alignment of value
33764      */
33765     valueAlign : "left",
33766
33767     getAutoCreate : function()
33768     {
33769         var hiddenInput = {
33770             tag: 'input',
33771             type: 'hidden',
33772             id: Roo.id(),
33773             cls: 'hidden-number-input'
33774         };
33775         
33776         if (this.name) {
33777             hiddenInput.name = this.name;
33778         }
33779         
33780         this.name = '';
33781         
33782         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33783         
33784         this.name = hiddenInput.name;
33785         
33786         if(cfg.cn.length > 0) {
33787             cfg.cn.push(hiddenInput);
33788         }
33789         
33790         return cfg;
33791     },
33792
33793     // private
33794     initEvents : function()
33795     {   
33796         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33797         
33798         var allowed = "0123456789";
33799         
33800         if(this.allowDecimals){
33801             allowed += this.decimalSeparator;
33802         }
33803         
33804         if(this.allowNegative){
33805             allowed += "-";
33806         }
33807         
33808         if(this.thousandsDelimiter) {
33809             allowed += ",";
33810         }
33811         
33812         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33813         
33814         var keyPress = function(e){
33815             
33816             var k = e.getKey();
33817             
33818             var c = e.getCharCode();
33819             
33820             if(
33821                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33822                     allowed.indexOf(String.fromCharCode(c)) === -1
33823             ){
33824                 e.stopEvent();
33825                 return;
33826             }
33827             
33828             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33829                 return;
33830             }
33831             
33832             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33833                 e.stopEvent();
33834             }
33835         };
33836         
33837         this.el.on("keypress", keyPress, this);
33838     },
33839     
33840     validateValue : function(value)
33841     {
33842         
33843         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33844             return false;
33845         }
33846         
33847         var num = this.parseValue(value);
33848         
33849         if(isNaN(num)){
33850             this.markInvalid(String.format(this.nanText, value));
33851             return false;
33852         }
33853         
33854         if(num < this.minValue){
33855             this.markInvalid(String.format(this.minText, this.minValue));
33856             return false;
33857         }
33858         
33859         if(num > this.maxValue){
33860             this.markInvalid(String.format(this.maxText, this.maxValue));
33861             return false;
33862         }
33863         
33864         return true;
33865     },
33866
33867     getValue : function()
33868     {
33869         var v = this.hiddenEl().getValue();
33870         
33871         return this.fixPrecision(this.parseValue(v));
33872     },
33873
33874     parseValue : function(value)
33875     {
33876         if(this.thousandsDelimiter) {
33877             value += "";
33878             r = new RegExp(",", "g");
33879             value = value.replace(r, "");
33880         }
33881         
33882         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33883         return isNaN(value) ? '' : value;
33884     },
33885
33886     fixPrecision : function(value)
33887     {
33888         if(this.thousandsDelimiter) {
33889             value += "";
33890             r = new RegExp(",", "g");
33891             value = value.replace(r, "");
33892         }
33893         
33894         var nan = isNaN(value);
33895         
33896         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33897             return nan ? '' : value;
33898         }
33899         return parseFloat(value).toFixed(this.decimalPrecision);
33900     },
33901
33902     setValue : function(v)
33903     {
33904         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33905         
33906         this.value = v;
33907         
33908         if(this.rendered){
33909             
33910             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33911             
33912             this.inputEl().dom.value = (v == '') ? '' :
33913                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33914             
33915             if(!this.allowZero && v === '0') {
33916                 this.hiddenEl().dom.value = '';
33917                 this.inputEl().dom.value = '';
33918             }
33919             
33920             this.validate();
33921         }
33922     },
33923
33924     decimalPrecisionFcn : function(v)
33925     {
33926         return Math.floor(v);
33927     },
33928
33929     beforeBlur : function()
33930     {
33931         var v = this.parseValue(this.getRawValue());
33932         
33933         if(v || v === 0 || v === ''){
33934             this.setValue(v);
33935         }
33936     },
33937     
33938     hiddenEl : function()
33939     {
33940         return this.el.select('input.hidden-number-input',true).first();
33941     }
33942     
33943 });
33944
33945  
33946
33947 /*
33948 * Licence: LGPL
33949 */
33950
33951 /**
33952  * @class Roo.bootstrap.DocumentSlider
33953  * @extends Roo.bootstrap.Component
33954  * Bootstrap DocumentSlider class
33955  * 
33956  * @constructor
33957  * Create a new DocumentViewer
33958  * @param {Object} config The config object
33959  */
33960
33961 Roo.bootstrap.DocumentSlider = function(config){
33962     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33963     
33964     this.files = [];
33965     
33966     this.addEvents({
33967         /**
33968          * @event initial
33969          * Fire after initEvent
33970          * @param {Roo.bootstrap.DocumentSlider} this
33971          */
33972         "initial" : true,
33973         /**
33974          * @event update
33975          * Fire after update
33976          * @param {Roo.bootstrap.DocumentSlider} this
33977          */
33978         "update" : true,
33979         /**
33980          * @event click
33981          * Fire after click
33982          * @param {Roo.bootstrap.DocumentSlider} this
33983          */
33984         "click" : true
33985     });
33986 };
33987
33988 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33989     
33990     files : false,
33991     
33992     indicator : 0,
33993     
33994     getAutoCreate : function()
33995     {
33996         var cfg = {
33997             tag : 'div',
33998             cls : 'roo-document-slider',
33999             cn : [
34000                 {
34001                     tag : 'div',
34002                     cls : 'roo-document-slider-header',
34003                     cn : [
34004                         {
34005                             tag : 'div',
34006                             cls : 'roo-document-slider-header-title'
34007                         }
34008                     ]
34009                 },
34010                 {
34011                     tag : 'div',
34012                     cls : 'roo-document-slider-body',
34013                     cn : [
34014                         {
34015                             tag : 'div',
34016                             cls : 'roo-document-slider-prev',
34017                             cn : [
34018                                 {
34019                                     tag : 'i',
34020                                     cls : 'fa fa-chevron-left'
34021                                 }
34022                             ]
34023                         },
34024                         {
34025                             tag : 'div',
34026                             cls : 'roo-document-slider-thumb',
34027                             cn : [
34028                                 {
34029                                     tag : 'img',
34030                                     cls : 'roo-document-slider-image'
34031                                 }
34032                             ]
34033                         },
34034                         {
34035                             tag : 'div',
34036                             cls : 'roo-document-slider-next',
34037                             cn : [
34038                                 {
34039                                     tag : 'i',
34040                                     cls : 'fa fa-chevron-right'
34041                                 }
34042                             ]
34043                         }
34044                     ]
34045                 }
34046             ]
34047         };
34048         
34049         return cfg;
34050     },
34051     
34052     initEvents : function()
34053     {
34054         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34055         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34056         
34057         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34058         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34059         
34060         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34061         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34062         
34063         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34064         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34065         
34066         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34067         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34068         
34069         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34070         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34071         
34072         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34073         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34074         
34075         this.thumbEl.on('click', this.onClick, this);
34076         
34077         this.prevIndicator.on('click', this.prev, this);
34078         
34079         this.nextIndicator.on('click', this.next, this);
34080         
34081     },
34082     
34083     initial : function()
34084     {
34085         if(this.files.length){
34086             this.indicator = 1;
34087             this.update()
34088         }
34089         
34090         this.fireEvent('initial', this);
34091     },
34092     
34093     update : function()
34094     {
34095         this.imageEl.attr('src', this.files[this.indicator - 1]);
34096         
34097         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34098         
34099         this.prevIndicator.show();
34100         
34101         if(this.indicator == 1){
34102             this.prevIndicator.hide();
34103         }
34104         
34105         this.nextIndicator.show();
34106         
34107         if(this.indicator == this.files.length){
34108             this.nextIndicator.hide();
34109         }
34110         
34111         this.thumbEl.scrollTo('top');
34112         
34113         this.fireEvent('update', this);
34114     },
34115     
34116     onClick : function(e)
34117     {
34118         e.preventDefault();
34119         
34120         this.fireEvent('click', this);
34121     },
34122     
34123     prev : function(e)
34124     {
34125         e.preventDefault();
34126         
34127         this.indicator = Math.max(1, this.indicator - 1);
34128         
34129         this.update();
34130     },
34131     
34132     next : function(e)
34133     {
34134         e.preventDefault();
34135         
34136         this.indicator = Math.min(this.files.length, this.indicator + 1);
34137         
34138         this.update();
34139     }
34140 });
34141 /*
34142  * - LGPL
34143  *
34144  * RadioSet
34145  *
34146  *
34147  */
34148
34149 /**
34150  * @class Roo.bootstrap.RadioSet
34151  * @extends Roo.bootstrap.Input
34152  * Bootstrap RadioSet class
34153  * @cfg {String} indicatorpos (left|right) default left
34154  * @cfg {Boolean} inline (true|false) inline the element (default true)
34155  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34156  * @constructor
34157  * Create a new RadioSet
34158  * @param {Object} config The config object
34159  */
34160
34161 Roo.bootstrap.RadioSet = function(config){
34162     
34163     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34164     
34165     this.radioes = [];
34166     
34167     Roo.bootstrap.RadioSet.register(this);
34168     
34169     this.addEvents({
34170         /**
34171         * @event check
34172         * Fires when the element is checked or unchecked.
34173         * @param {Roo.bootstrap.RadioSet} this This radio
34174         * @param {Roo.bootstrap.Radio} item The checked item
34175         */
34176        check : true,
34177        /**
34178         * @event click
34179         * Fires when the element is click.
34180         * @param {Roo.bootstrap.RadioSet} this This radio set
34181         * @param {Roo.bootstrap.Radio} item The checked item
34182         * @param {Roo.EventObject} e The event object
34183         */
34184        click : true
34185     });
34186     
34187 };
34188
34189 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34190
34191     radioes : false,
34192     
34193     inline : true,
34194     
34195     weight : '',
34196     
34197     indicatorpos : 'left',
34198     
34199     getAutoCreate : function()
34200     {
34201         var label = {
34202             tag : 'label',
34203             cls : 'roo-radio-set-label',
34204             cn : [
34205                 {
34206                     tag : 'span',
34207                     html : this.fieldLabel
34208                 }
34209             ]
34210         };
34211         if (Roo.bootstrap.version == 3) {
34212             
34213             
34214             if(this.indicatorpos == 'left'){
34215                 label.cn.unshift({
34216                     tag : 'i',
34217                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34218                     tooltip : 'This field is required'
34219                 });
34220             } else {
34221                 label.cn.push({
34222                     tag : 'i',
34223                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34224                     tooltip : 'This field is required'
34225                 });
34226             }
34227         }
34228         var items = {
34229             tag : 'div',
34230             cls : 'roo-radio-set-items'
34231         };
34232         
34233         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34234         
34235         if (align === 'left' && this.fieldLabel.length) {
34236             
34237             items = {
34238                 cls : "roo-radio-set-right", 
34239                 cn: [
34240                     items
34241                 ]
34242             };
34243             
34244             if(this.labelWidth > 12){
34245                 label.style = "width: " + this.labelWidth + 'px';
34246             }
34247             
34248             if(this.labelWidth < 13 && this.labelmd == 0){
34249                 this.labelmd = this.labelWidth;
34250             }
34251             
34252             if(this.labellg > 0){
34253                 label.cls += ' col-lg-' + this.labellg;
34254                 items.cls += ' col-lg-' + (12 - this.labellg);
34255             }
34256             
34257             if(this.labelmd > 0){
34258                 label.cls += ' col-md-' + this.labelmd;
34259                 items.cls += ' col-md-' + (12 - this.labelmd);
34260             }
34261             
34262             if(this.labelsm > 0){
34263                 label.cls += ' col-sm-' + this.labelsm;
34264                 items.cls += ' col-sm-' + (12 - this.labelsm);
34265             }
34266             
34267             if(this.labelxs > 0){
34268                 label.cls += ' col-xs-' + this.labelxs;
34269                 items.cls += ' col-xs-' + (12 - this.labelxs);
34270             }
34271         }
34272         
34273         var cfg = {
34274             tag : 'div',
34275             cls : 'roo-radio-set',
34276             cn : [
34277                 {
34278                     tag : 'input',
34279                     cls : 'roo-radio-set-input',
34280                     type : 'hidden',
34281                     name : this.name,
34282                     value : this.value ? this.value :  ''
34283                 },
34284                 label,
34285                 items
34286             ]
34287         };
34288         
34289         if(this.weight.length){
34290             cfg.cls += ' roo-radio-' + this.weight;
34291         }
34292         
34293         if(this.inline) {
34294             cfg.cls += ' roo-radio-set-inline';
34295         }
34296         
34297         var settings=this;
34298         ['xs','sm','md','lg'].map(function(size){
34299             if (settings[size]) {
34300                 cfg.cls += ' col-' + size + '-' + settings[size];
34301             }
34302         });
34303         
34304         return cfg;
34305         
34306     },
34307
34308     initEvents : function()
34309     {
34310         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34311         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34312         
34313         if(!this.fieldLabel.length){
34314             this.labelEl.hide();
34315         }
34316         
34317         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34318         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34319         
34320         this.indicator = this.indicatorEl();
34321         
34322         if(this.indicator){
34323             this.indicator.addClass('invisible');
34324         }
34325         
34326         this.originalValue = this.getValue();
34327         
34328     },
34329     
34330     inputEl: function ()
34331     {
34332         return this.el.select('.roo-radio-set-input', true).first();
34333     },
34334     
34335     getChildContainer : function()
34336     {
34337         return this.itemsEl;
34338     },
34339     
34340     register : function(item)
34341     {
34342         this.radioes.push(item);
34343         
34344     },
34345     
34346     validate : function()
34347     {   
34348         if(this.getVisibilityEl().hasClass('hidden')){
34349             return true;
34350         }
34351         
34352         var valid = false;
34353         
34354         Roo.each(this.radioes, function(i){
34355             if(!i.checked){
34356                 return;
34357             }
34358             
34359             valid = true;
34360             return false;
34361         });
34362         
34363         if(this.allowBlank) {
34364             return true;
34365         }
34366         
34367         if(this.disabled || valid){
34368             this.markValid();
34369             return true;
34370         }
34371         
34372         this.markInvalid();
34373         return false;
34374         
34375     },
34376     
34377     markValid : function()
34378     {
34379         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34380             this.indicatorEl().removeClass('visible');
34381             this.indicatorEl().addClass('invisible');
34382         }
34383         
34384         
34385         if (Roo.bootstrap.version == 3) {
34386             this.el.removeClass([this.invalidClass, this.validClass]);
34387             this.el.addClass(this.validClass);
34388         } else {
34389             this.el.removeClass(['is-invalid','is-valid']);
34390             this.el.addClass(['is-valid']);
34391         }
34392         this.fireEvent('valid', this);
34393     },
34394     
34395     markInvalid : function(msg)
34396     {
34397         if(this.allowBlank || this.disabled){
34398             return;
34399         }
34400         
34401         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34402             this.indicatorEl().removeClass('invisible');
34403             this.indicatorEl().addClass('visible');
34404         }
34405         if (Roo.bootstrap.version == 3) {
34406             this.el.removeClass([this.invalidClass, this.validClass]);
34407             this.el.addClass(this.invalidClass);
34408         } else {
34409             this.el.removeClass(['is-invalid','is-valid']);
34410             this.el.addClass(['is-invalid']);
34411         }
34412         
34413         this.fireEvent('invalid', this, msg);
34414         
34415     },
34416     
34417     setValue : function(v, suppressEvent)
34418     {   
34419         if(this.value === v){
34420             return;
34421         }
34422         
34423         this.value = v;
34424         
34425         if(this.rendered){
34426             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34427         }
34428         
34429         Roo.each(this.radioes, function(i){
34430             i.checked = false;
34431             i.el.removeClass('checked');
34432         });
34433         
34434         Roo.each(this.radioes, function(i){
34435             
34436             if(i.value === v || i.value.toString() === v.toString()){
34437                 i.checked = true;
34438                 i.el.addClass('checked');
34439                 
34440                 if(suppressEvent !== true){
34441                     this.fireEvent('check', this, i);
34442                 }
34443                 
34444                 return false;
34445             }
34446             
34447         }, this);
34448         
34449         this.validate();
34450     },
34451     
34452     clearInvalid : function(){
34453         
34454         if(!this.el || this.preventMark){
34455             return;
34456         }
34457         
34458         this.el.removeClass([this.invalidClass]);
34459         
34460         this.fireEvent('valid', this);
34461     }
34462     
34463 });
34464
34465 Roo.apply(Roo.bootstrap.RadioSet, {
34466     
34467     groups: {},
34468     
34469     register : function(set)
34470     {
34471         this.groups[set.name] = set;
34472     },
34473     
34474     get: function(name) 
34475     {
34476         if (typeof(this.groups[name]) == 'undefined') {
34477             return false;
34478         }
34479         
34480         return this.groups[name] ;
34481     }
34482     
34483 });
34484 /*
34485  * Based on:
34486  * Ext JS Library 1.1.1
34487  * Copyright(c) 2006-2007, Ext JS, LLC.
34488  *
34489  * Originally Released Under LGPL - original licence link has changed is not relivant.
34490  *
34491  * Fork - LGPL
34492  * <script type="text/javascript">
34493  */
34494
34495
34496 /**
34497  * @class Roo.bootstrap.SplitBar
34498  * @extends Roo.util.Observable
34499  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34500  * <br><br>
34501  * Usage:
34502  * <pre><code>
34503 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34504                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34505 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34506 split.minSize = 100;
34507 split.maxSize = 600;
34508 split.animate = true;
34509 split.on('moved', splitterMoved);
34510 </code></pre>
34511  * @constructor
34512  * Create a new SplitBar
34513  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34514  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34515  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34516  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34517                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34518                         position of the SplitBar).
34519  */
34520 Roo.bootstrap.SplitBar = function(cfg){
34521     
34522     /** @private */
34523     
34524     //{
34525     //  dragElement : elm
34526     //  resizingElement: el,
34527         // optional..
34528     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34529     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34530         // existingProxy ???
34531     //}
34532     
34533     this.el = Roo.get(cfg.dragElement, true);
34534     this.el.dom.unselectable = "on";
34535     /** @private */
34536     this.resizingEl = Roo.get(cfg.resizingElement, true);
34537
34538     /**
34539      * @private
34540      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34541      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34542      * @type Number
34543      */
34544     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34545     
34546     /**
34547      * The minimum size of the resizing element. (Defaults to 0)
34548      * @type Number
34549      */
34550     this.minSize = 0;
34551     
34552     /**
34553      * The maximum size of the resizing element. (Defaults to 2000)
34554      * @type Number
34555      */
34556     this.maxSize = 2000;
34557     
34558     /**
34559      * Whether to animate the transition to the new size
34560      * @type Boolean
34561      */
34562     this.animate = false;
34563     
34564     /**
34565      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34566      * @type Boolean
34567      */
34568     this.useShim = false;
34569     
34570     /** @private */
34571     this.shim = null;
34572     
34573     if(!cfg.existingProxy){
34574         /** @private */
34575         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34576     }else{
34577         this.proxy = Roo.get(cfg.existingProxy).dom;
34578     }
34579     /** @private */
34580     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34581     
34582     /** @private */
34583     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34584     
34585     /** @private */
34586     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34587     
34588     /** @private */
34589     this.dragSpecs = {};
34590     
34591     /**
34592      * @private The adapter to use to positon and resize elements
34593      */
34594     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34595     this.adapter.init(this);
34596     
34597     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34598         /** @private */
34599         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34600         this.el.addClass("roo-splitbar-h");
34601     }else{
34602         /** @private */
34603         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34604         this.el.addClass("roo-splitbar-v");
34605     }
34606     
34607     this.addEvents({
34608         /**
34609          * @event resize
34610          * Fires when the splitter is moved (alias for {@link #event-moved})
34611          * @param {Roo.bootstrap.SplitBar} this
34612          * @param {Number} newSize the new width or height
34613          */
34614         "resize" : true,
34615         /**
34616          * @event moved
34617          * Fires when the splitter is moved
34618          * @param {Roo.bootstrap.SplitBar} this
34619          * @param {Number} newSize the new width or height
34620          */
34621         "moved" : true,
34622         /**
34623          * @event beforeresize
34624          * Fires before the splitter is dragged
34625          * @param {Roo.bootstrap.SplitBar} this
34626          */
34627         "beforeresize" : true,
34628
34629         "beforeapply" : true
34630     });
34631
34632     Roo.util.Observable.call(this);
34633 };
34634
34635 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34636     onStartProxyDrag : function(x, y){
34637         this.fireEvent("beforeresize", this);
34638         if(!this.overlay){
34639             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34640             o.unselectable();
34641             o.enableDisplayMode("block");
34642             // all splitbars share the same overlay
34643             Roo.bootstrap.SplitBar.prototype.overlay = o;
34644         }
34645         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34646         this.overlay.show();
34647         Roo.get(this.proxy).setDisplayed("block");
34648         var size = this.adapter.getElementSize(this);
34649         this.activeMinSize = this.getMinimumSize();;
34650         this.activeMaxSize = this.getMaximumSize();;
34651         var c1 = size - this.activeMinSize;
34652         var c2 = Math.max(this.activeMaxSize - size, 0);
34653         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34654             this.dd.resetConstraints();
34655             this.dd.setXConstraint(
34656                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34657                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34658             );
34659             this.dd.setYConstraint(0, 0);
34660         }else{
34661             this.dd.resetConstraints();
34662             this.dd.setXConstraint(0, 0);
34663             this.dd.setYConstraint(
34664                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34665                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34666             );
34667          }
34668         this.dragSpecs.startSize = size;
34669         this.dragSpecs.startPoint = [x, y];
34670         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34671     },
34672     
34673     /** 
34674      * @private Called after the drag operation by the DDProxy
34675      */
34676     onEndProxyDrag : function(e){
34677         Roo.get(this.proxy).setDisplayed(false);
34678         var endPoint = Roo.lib.Event.getXY(e);
34679         if(this.overlay){
34680             this.overlay.hide();
34681         }
34682         var newSize;
34683         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34684             newSize = this.dragSpecs.startSize + 
34685                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34686                     endPoint[0] - this.dragSpecs.startPoint[0] :
34687                     this.dragSpecs.startPoint[0] - endPoint[0]
34688                 );
34689         }else{
34690             newSize = this.dragSpecs.startSize + 
34691                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34692                     endPoint[1] - this.dragSpecs.startPoint[1] :
34693                     this.dragSpecs.startPoint[1] - endPoint[1]
34694                 );
34695         }
34696         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34697         if(newSize != this.dragSpecs.startSize){
34698             if(this.fireEvent('beforeapply', this, newSize) !== false){
34699                 this.adapter.setElementSize(this, newSize);
34700                 this.fireEvent("moved", this, newSize);
34701                 this.fireEvent("resize", this, newSize);
34702             }
34703         }
34704     },
34705     
34706     /**
34707      * Get the adapter this SplitBar uses
34708      * @return The adapter object
34709      */
34710     getAdapter : function(){
34711         return this.adapter;
34712     },
34713     
34714     /**
34715      * Set the adapter this SplitBar uses
34716      * @param {Object} adapter A SplitBar adapter object
34717      */
34718     setAdapter : function(adapter){
34719         this.adapter = adapter;
34720         this.adapter.init(this);
34721     },
34722     
34723     /**
34724      * Gets the minimum size for the resizing element
34725      * @return {Number} The minimum size
34726      */
34727     getMinimumSize : function(){
34728         return this.minSize;
34729     },
34730     
34731     /**
34732      * Sets the minimum size for the resizing element
34733      * @param {Number} minSize The minimum size
34734      */
34735     setMinimumSize : function(minSize){
34736         this.minSize = minSize;
34737     },
34738     
34739     /**
34740      * Gets the maximum size for the resizing element
34741      * @return {Number} The maximum size
34742      */
34743     getMaximumSize : function(){
34744         return this.maxSize;
34745     },
34746     
34747     /**
34748      * Sets the maximum size for the resizing element
34749      * @param {Number} maxSize The maximum size
34750      */
34751     setMaximumSize : function(maxSize){
34752         this.maxSize = maxSize;
34753     },
34754     
34755     /**
34756      * Sets the initialize size for the resizing element
34757      * @param {Number} size The initial size
34758      */
34759     setCurrentSize : function(size){
34760         var oldAnimate = this.animate;
34761         this.animate = false;
34762         this.adapter.setElementSize(this, size);
34763         this.animate = oldAnimate;
34764     },
34765     
34766     /**
34767      * Destroy this splitbar. 
34768      * @param {Boolean} removeEl True to remove the element
34769      */
34770     destroy : function(removeEl){
34771         if(this.shim){
34772             this.shim.remove();
34773         }
34774         this.dd.unreg();
34775         this.proxy.parentNode.removeChild(this.proxy);
34776         if(removeEl){
34777             this.el.remove();
34778         }
34779     }
34780 });
34781
34782 /**
34783  * @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.
34784  */
34785 Roo.bootstrap.SplitBar.createProxy = function(dir){
34786     var proxy = new Roo.Element(document.createElement("div"));
34787     proxy.unselectable();
34788     var cls = 'roo-splitbar-proxy';
34789     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34790     document.body.appendChild(proxy.dom);
34791     return proxy.dom;
34792 };
34793
34794 /** 
34795  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34796  * Default Adapter. It assumes the splitter and resizing element are not positioned
34797  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34798  */
34799 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34800 };
34801
34802 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34803     // do nothing for now
34804     init : function(s){
34805     
34806     },
34807     /**
34808      * Called before drag operations to get the current size of the resizing element. 
34809      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34810      */
34811      getElementSize : function(s){
34812         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34813             return s.resizingEl.getWidth();
34814         }else{
34815             return s.resizingEl.getHeight();
34816         }
34817     },
34818     
34819     /**
34820      * Called after drag operations to set the size of the resizing element.
34821      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34822      * @param {Number} newSize The new size to set
34823      * @param {Function} onComplete A function to be invoked when resizing is complete
34824      */
34825     setElementSize : function(s, newSize, onComplete){
34826         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34827             if(!s.animate){
34828                 s.resizingEl.setWidth(newSize);
34829                 if(onComplete){
34830                     onComplete(s, newSize);
34831                 }
34832             }else{
34833                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34834             }
34835         }else{
34836             
34837             if(!s.animate){
34838                 s.resizingEl.setHeight(newSize);
34839                 if(onComplete){
34840                     onComplete(s, newSize);
34841                 }
34842             }else{
34843                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34844             }
34845         }
34846     }
34847 };
34848
34849 /** 
34850  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34851  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34852  * Adapter that  moves the splitter element to align with the resized sizing element. 
34853  * Used with an absolute positioned SplitBar.
34854  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34855  * document.body, make sure you assign an id to the body element.
34856  */
34857 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34858     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34859     this.container = Roo.get(container);
34860 };
34861
34862 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34863     init : function(s){
34864         this.basic.init(s);
34865     },
34866     
34867     getElementSize : function(s){
34868         return this.basic.getElementSize(s);
34869     },
34870     
34871     setElementSize : function(s, newSize, onComplete){
34872         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34873     },
34874     
34875     moveSplitter : function(s){
34876         var yes = Roo.bootstrap.SplitBar;
34877         switch(s.placement){
34878             case yes.LEFT:
34879                 s.el.setX(s.resizingEl.getRight());
34880                 break;
34881             case yes.RIGHT:
34882                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34883                 break;
34884             case yes.TOP:
34885                 s.el.setY(s.resizingEl.getBottom());
34886                 break;
34887             case yes.BOTTOM:
34888                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34889                 break;
34890         }
34891     }
34892 };
34893
34894 /**
34895  * Orientation constant - Create a vertical SplitBar
34896  * @static
34897  * @type Number
34898  */
34899 Roo.bootstrap.SplitBar.VERTICAL = 1;
34900
34901 /**
34902  * Orientation constant - Create a horizontal SplitBar
34903  * @static
34904  * @type Number
34905  */
34906 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34907
34908 /**
34909  * Placement constant - The resizing element is to the left of the splitter element
34910  * @static
34911  * @type Number
34912  */
34913 Roo.bootstrap.SplitBar.LEFT = 1;
34914
34915 /**
34916  * Placement constant - The resizing element is to the right of the splitter element
34917  * @static
34918  * @type Number
34919  */
34920 Roo.bootstrap.SplitBar.RIGHT = 2;
34921
34922 /**
34923  * Placement constant - The resizing element is positioned above the splitter element
34924  * @static
34925  * @type Number
34926  */
34927 Roo.bootstrap.SplitBar.TOP = 3;
34928
34929 /**
34930  * Placement constant - The resizing element is positioned under splitter element
34931  * @static
34932  * @type Number
34933  */
34934 Roo.bootstrap.SplitBar.BOTTOM = 4;
34935 Roo.namespace("Roo.bootstrap.layout");/*
34936  * Based on:
34937  * Ext JS Library 1.1.1
34938  * Copyright(c) 2006-2007, Ext JS, LLC.
34939  *
34940  * Originally Released Under LGPL - original licence link has changed is not relivant.
34941  *
34942  * Fork - LGPL
34943  * <script type="text/javascript">
34944  */
34945
34946 /**
34947  * @class Roo.bootstrap.layout.Manager
34948  * @extends Roo.bootstrap.Component
34949  * Base class for layout managers.
34950  */
34951 Roo.bootstrap.layout.Manager = function(config)
34952 {
34953     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34954
34955
34956
34957
34958
34959     /** false to disable window resize monitoring @type Boolean */
34960     this.monitorWindowResize = true;
34961     this.regions = {};
34962     this.addEvents({
34963         /**
34964          * @event layout
34965          * Fires when a layout is performed.
34966          * @param {Roo.LayoutManager} this
34967          */
34968         "layout" : true,
34969         /**
34970          * @event regionresized
34971          * Fires when the user resizes a region.
34972          * @param {Roo.LayoutRegion} region The resized region
34973          * @param {Number} newSize The new size (width for east/west, height for north/south)
34974          */
34975         "regionresized" : true,
34976         /**
34977          * @event regioncollapsed
34978          * Fires when a region is collapsed.
34979          * @param {Roo.LayoutRegion} region The collapsed region
34980          */
34981         "regioncollapsed" : true,
34982         /**
34983          * @event regionexpanded
34984          * Fires when a region is expanded.
34985          * @param {Roo.LayoutRegion} region The expanded region
34986          */
34987         "regionexpanded" : true
34988     });
34989     this.updating = false;
34990
34991     if (config.el) {
34992         this.el = Roo.get(config.el);
34993         this.initEvents();
34994     }
34995
34996 };
34997
34998 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34999
35000
35001     regions : null,
35002
35003     monitorWindowResize : true,
35004
35005
35006     updating : false,
35007
35008
35009     onRender : function(ct, position)
35010     {
35011         if(!this.el){
35012             this.el = Roo.get(ct);
35013             this.initEvents();
35014         }
35015         //this.fireEvent('render',this);
35016     },
35017
35018
35019     initEvents: function()
35020     {
35021
35022
35023         // ie scrollbar fix
35024         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35025             document.body.scroll = "no";
35026         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35027             this.el.position('relative');
35028         }
35029         this.id = this.el.id;
35030         this.el.addClass("roo-layout-container");
35031         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35032         if(this.el.dom != document.body ) {
35033             this.el.on('resize', this.layout,this);
35034             this.el.on('show', this.layout,this);
35035         }
35036
35037     },
35038
35039     /**
35040      * Returns true if this layout is currently being updated
35041      * @return {Boolean}
35042      */
35043     isUpdating : function(){
35044         return this.updating;
35045     },
35046
35047     /**
35048      * Suspend the LayoutManager from doing auto-layouts while
35049      * making multiple add or remove calls
35050      */
35051     beginUpdate : function(){
35052         this.updating = true;
35053     },
35054
35055     /**
35056      * Restore auto-layouts and optionally disable the manager from performing a layout
35057      * @param {Boolean} noLayout true to disable a layout update
35058      */
35059     endUpdate : function(noLayout){
35060         this.updating = false;
35061         if(!noLayout){
35062             this.layout();
35063         }
35064     },
35065
35066     layout: function(){
35067         // abstract...
35068     },
35069
35070     onRegionResized : function(region, newSize){
35071         this.fireEvent("regionresized", region, newSize);
35072         this.layout();
35073     },
35074
35075     onRegionCollapsed : function(region){
35076         this.fireEvent("regioncollapsed", region);
35077     },
35078
35079     onRegionExpanded : function(region){
35080         this.fireEvent("regionexpanded", region);
35081     },
35082
35083     /**
35084      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35085      * performs box-model adjustments.
35086      * @return {Object} The size as an object {width: (the width), height: (the height)}
35087      */
35088     getViewSize : function()
35089     {
35090         var size;
35091         if(this.el.dom != document.body){
35092             size = this.el.getSize();
35093         }else{
35094             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35095         }
35096         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35097         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35098         return size;
35099     },
35100
35101     /**
35102      * Returns the Element this layout is bound to.
35103      * @return {Roo.Element}
35104      */
35105     getEl : function(){
35106         return this.el;
35107     },
35108
35109     /**
35110      * Returns the specified region.
35111      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35112      * @return {Roo.LayoutRegion}
35113      */
35114     getRegion : function(target){
35115         return this.regions[target.toLowerCase()];
35116     },
35117
35118     onWindowResize : function(){
35119         if(this.monitorWindowResize){
35120             this.layout();
35121         }
35122     }
35123 });
35124 /*
35125  * Based on:
35126  * Ext JS Library 1.1.1
35127  * Copyright(c) 2006-2007, Ext JS, LLC.
35128  *
35129  * Originally Released Under LGPL - original licence link has changed is not relivant.
35130  *
35131  * Fork - LGPL
35132  * <script type="text/javascript">
35133  */
35134 /**
35135  * @class Roo.bootstrap.layout.Border
35136  * @extends Roo.bootstrap.layout.Manager
35137  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35138  * please see: examples/bootstrap/nested.html<br><br>
35139  
35140 <b>The container the layout is rendered into can be either the body element or any other element.
35141 If it is not the body element, the container needs to either be an absolute positioned element,
35142 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35143 the container size if it is not the body element.</b>
35144
35145 * @constructor
35146 * Create a new Border
35147 * @param {Object} config Configuration options
35148  */
35149 Roo.bootstrap.layout.Border = function(config){
35150     config = config || {};
35151     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35152     
35153     
35154     
35155     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35156         if(config[region]){
35157             config[region].region = region;
35158             this.addRegion(config[region]);
35159         }
35160     },this);
35161     
35162 };
35163
35164 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35165
35166 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35167     
35168     parent : false, // this might point to a 'nest' or a ???
35169     
35170     /**
35171      * Creates and adds a new region if it doesn't already exist.
35172      * @param {String} target The target region key (north, south, east, west or center).
35173      * @param {Object} config The regions config object
35174      * @return {BorderLayoutRegion} The new region
35175      */
35176     addRegion : function(config)
35177     {
35178         if(!this.regions[config.region]){
35179             var r = this.factory(config);
35180             this.bindRegion(r);
35181         }
35182         return this.regions[config.region];
35183     },
35184
35185     // private (kinda)
35186     bindRegion : function(r){
35187         this.regions[r.config.region] = r;
35188         
35189         r.on("visibilitychange",    this.layout, this);
35190         r.on("paneladded",          this.layout, this);
35191         r.on("panelremoved",        this.layout, this);
35192         r.on("invalidated",         this.layout, this);
35193         r.on("resized",             this.onRegionResized, this);
35194         r.on("collapsed",           this.onRegionCollapsed, this);
35195         r.on("expanded",            this.onRegionExpanded, this);
35196     },
35197
35198     /**
35199      * Performs a layout update.
35200      */
35201     layout : function()
35202     {
35203         if(this.updating) {
35204             return;
35205         }
35206         
35207         // render all the rebions if they have not been done alreayd?
35208         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35209             if(this.regions[region] && !this.regions[region].bodyEl){
35210                 this.regions[region].onRender(this.el)
35211             }
35212         },this);
35213         
35214         var size = this.getViewSize();
35215         var w = size.width;
35216         var h = size.height;
35217         var centerW = w;
35218         var centerH = h;
35219         var centerY = 0;
35220         var centerX = 0;
35221         //var x = 0, y = 0;
35222
35223         var rs = this.regions;
35224         var north = rs["north"];
35225         var south = rs["south"]; 
35226         var west = rs["west"];
35227         var east = rs["east"];
35228         var center = rs["center"];
35229         //if(this.hideOnLayout){ // not supported anymore
35230             //c.el.setStyle("display", "none");
35231         //}
35232         if(north && north.isVisible()){
35233             var b = north.getBox();
35234             var m = north.getMargins();
35235             b.width = w - (m.left+m.right);
35236             b.x = m.left;
35237             b.y = m.top;
35238             centerY = b.height + b.y + m.bottom;
35239             centerH -= centerY;
35240             north.updateBox(this.safeBox(b));
35241         }
35242         if(south && south.isVisible()){
35243             var b = south.getBox();
35244             var m = south.getMargins();
35245             b.width = w - (m.left+m.right);
35246             b.x = m.left;
35247             var totalHeight = (b.height + m.top + m.bottom);
35248             b.y = h - totalHeight + m.top;
35249             centerH -= totalHeight;
35250             south.updateBox(this.safeBox(b));
35251         }
35252         if(west && west.isVisible()){
35253             var b = west.getBox();
35254             var m = west.getMargins();
35255             b.height = centerH - (m.top+m.bottom);
35256             b.x = m.left;
35257             b.y = centerY + m.top;
35258             var totalWidth = (b.width + m.left + m.right);
35259             centerX += totalWidth;
35260             centerW -= totalWidth;
35261             west.updateBox(this.safeBox(b));
35262         }
35263         if(east && east.isVisible()){
35264             var b = east.getBox();
35265             var m = east.getMargins();
35266             b.height = centerH - (m.top+m.bottom);
35267             var totalWidth = (b.width + m.left + m.right);
35268             b.x = w - totalWidth + m.left;
35269             b.y = centerY + m.top;
35270             centerW -= totalWidth;
35271             east.updateBox(this.safeBox(b));
35272         }
35273         if(center){
35274             var m = center.getMargins();
35275             var centerBox = {
35276                 x: centerX + m.left,
35277                 y: centerY + m.top,
35278                 width: centerW - (m.left+m.right),
35279                 height: centerH - (m.top+m.bottom)
35280             };
35281             //if(this.hideOnLayout){
35282                 //center.el.setStyle("display", "block");
35283             //}
35284             center.updateBox(this.safeBox(centerBox));
35285         }
35286         this.el.repaint();
35287         this.fireEvent("layout", this);
35288     },
35289
35290     // private
35291     safeBox : function(box){
35292         box.width = Math.max(0, box.width);
35293         box.height = Math.max(0, box.height);
35294         return box;
35295     },
35296
35297     /**
35298      * Adds a ContentPanel (or subclass) to this layout.
35299      * @param {String} target The target region key (north, south, east, west or center).
35300      * @param {Roo.ContentPanel} panel The panel to add
35301      * @return {Roo.ContentPanel} The added panel
35302      */
35303     add : function(target, panel){
35304          
35305         target = target.toLowerCase();
35306         return this.regions[target].add(panel);
35307     },
35308
35309     /**
35310      * Remove a ContentPanel (or subclass) to this layout.
35311      * @param {String} target The target region key (north, south, east, west or center).
35312      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35313      * @return {Roo.ContentPanel} The removed panel
35314      */
35315     remove : function(target, panel){
35316         target = target.toLowerCase();
35317         return this.regions[target].remove(panel);
35318     },
35319
35320     /**
35321      * Searches all regions for a panel with the specified id
35322      * @param {String} panelId
35323      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35324      */
35325     findPanel : function(panelId){
35326         var rs = this.regions;
35327         for(var target in rs){
35328             if(typeof rs[target] != "function"){
35329                 var p = rs[target].getPanel(panelId);
35330                 if(p){
35331                     return p;
35332                 }
35333             }
35334         }
35335         return null;
35336     },
35337
35338     /**
35339      * Searches all regions for a panel with the specified id and activates (shows) it.
35340      * @param {String/ContentPanel} panelId The panels id or the panel itself
35341      * @return {Roo.ContentPanel} The shown panel or null
35342      */
35343     showPanel : function(panelId) {
35344       var rs = this.regions;
35345       for(var target in rs){
35346          var r = rs[target];
35347          if(typeof r != "function"){
35348             if(r.hasPanel(panelId)){
35349                return r.showPanel(panelId);
35350             }
35351          }
35352       }
35353       return null;
35354    },
35355
35356    /**
35357      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35358      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35359      */
35360    /*
35361     restoreState : function(provider){
35362         if(!provider){
35363             provider = Roo.state.Manager;
35364         }
35365         var sm = new Roo.LayoutStateManager();
35366         sm.init(this, provider);
35367     },
35368 */
35369  
35370  
35371     /**
35372      * Adds a xtype elements to the layout.
35373      * <pre><code>
35374
35375 layout.addxtype({
35376        xtype : 'ContentPanel',
35377        region: 'west',
35378        items: [ .... ]
35379    }
35380 );
35381
35382 layout.addxtype({
35383         xtype : 'NestedLayoutPanel',
35384         region: 'west',
35385         layout: {
35386            center: { },
35387            west: { }   
35388         },
35389         items : [ ... list of content panels or nested layout panels.. ]
35390    }
35391 );
35392 </code></pre>
35393      * @param {Object} cfg Xtype definition of item to add.
35394      */
35395     addxtype : function(cfg)
35396     {
35397         // basically accepts a pannel...
35398         // can accept a layout region..!?!?
35399         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35400         
35401         
35402         // theory?  children can only be panels??
35403         
35404         //if (!cfg.xtype.match(/Panel$/)) {
35405         //    return false;
35406         //}
35407         var ret = false;
35408         
35409         if (typeof(cfg.region) == 'undefined') {
35410             Roo.log("Failed to add Panel, region was not set");
35411             Roo.log(cfg);
35412             return false;
35413         }
35414         var region = cfg.region;
35415         delete cfg.region;
35416         
35417           
35418         var xitems = [];
35419         if (cfg.items) {
35420             xitems = cfg.items;
35421             delete cfg.items;
35422         }
35423         var nb = false;
35424         
35425         if ( region == 'center') {
35426             Roo.log("Center: " + cfg.title);
35427         }
35428         
35429         
35430         switch(cfg.xtype) 
35431         {
35432             case 'Content':  // ContentPanel (el, cfg)
35433             case 'Scroll':  // ContentPanel (el, cfg)
35434             case 'View': 
35435                 cfg.autoCreate = cfg.autoCreate || true;
35436                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35437                 //} else {
35438                 //    var el = this.el.createChild();
35439                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35440                 //}
35441                 
35442                 this.add(region, ret);
35443                 break;
35444             
35445             /*
35446             case 'TreePanel': // our new panel!
35447                 cfg.el = this.el.createChild();
35448                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35449                 this.add(region, ret);
35450                 break;
35451             */
35452             
35453             case 'Nest': 
35454                 // create a new Layout (which is  a Border Layout...
35455                 
35456                 var clayout = cfg.layout;
35457                 clayout.el  = this.el.createChild();
35458                 clayout.items   = clayout.items  || [];
35459                 
35460                 delete cfg.layout;
35461                 
35462                 // replace this exitems with the clayout ones..
35463                 xitems = clayout.items;
35464                  
35465                 // force background off if it's in center...
35466                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35467                     cfg.background = false;
35468                 }
35469                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35470                 
35471                 
35472                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35473                 //console.log('adding nested layout panel '  + cfg.toSource());
35474                 this.add(region, ret);
35475                 nb = {}; /// find first...
35476                 break;
35477             
35478             case 'Grid':
35479                 
35480                 // needs grid and region
35481                 
35482                 //var el = this.getRegion(region).el.createChild();
35483                 /*
35484                  *var el = this.el.createChild();
35485                 // create the grid first...
35486                 cfg.grid.container = el;
35487                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35488                 */
35489                 
35490                 if (region == 'center' && this.active ) {
35491                     cfg.background = false;
35492                 }
35493                 
35494                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35495                 
35496                 this.add(region, ret);
35497                 /*
35498                 if (cfg.background) {
35499                     // render grid on panel activation (if panel background)
35500                     ret.on('activate', function(gp) {
35501                         if (!gp.grid.rendered) {
35502                     //        gp.grid.render(el);
35503                         }
35504                     });
35505                 } else {
35506                   //  cfg.grid.render(el);
35507                 }
35508                 */
35509                 break;
35510            
35511            
35512             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35513                 // it was the old xcomponent building that caused this before.
35514                 // espeically if border is the top element in the tree.
35515                 ret = this;
35516                 break; 
35517                 
35518                     
35519                 
35520                 
35521                 
35522             default:
35523                 /*
35524                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35525                     
35526                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35527                     this.add(region, ret);
35528                 } else {
35529                 */
35530                     Roo.log(cfg);
35531                     throw "Can not add '" + cfg.xtype + "' to Border";
35532                     return null;
35533              
35534                                 
35535              
35536         }
35537         this.beginUpdate();
35538         // add children..
35539         var region = '';
35540         var abn = {};
35541         Roo.each(xitems, function(i)  {
35542             region = nb && i.region ? i.region : false;
35543             
35544             var add = ret.addxtype(i);
35545            
35546             if (region) {
35547                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35548                 if (!i.background) {
35549                     abn[region] = nb[region] ;
35550                 }
35551             }
35552             
35553         });
35554         this.endUpdate();
35555
35556         // make the last non-background panel active..
35557         //if (nb) { Roo.log(abn); }
35558         if (nb) {
35559             
35560             for(var r in abn) {
35561                 region = this.getRegion(r);
35562                 if (region) {
35563                     // tried using nb[r], but it does not work..
35564                      
35565                     region.showPanel(abn[r]);
35566                    
35567                 }
35568             }
35569         }
35570         return ret;
35571         
35572     },
35573     
35574     
35575 // private
35576     factory : function(cfg)
35577     {
35578         
35579         var validRegions = Roo.bootstrap.layout.Border.regions;
35580
35581         var target = cfg.region;
35582         cfg.mgr = this;
35583         
35584         var r = Roo.bootstrap.layout;
35585         Roo.log(target);
35586         switch(target){
35587             case "north":
35588                 return new r.North(cfg);
35589             case "south":
35590                 return new r.South(cfg);
35591             case "east":
35592                 return new r.East(cfg);
35593             case "west":
35594                 return new r.West(cfg);
35595             case "center":
35596                 return new r.Center(cfg);
35597         }
35598         throw 'Layout region "'+target+'" not supported.';
35599     }
35600     
35601     
35602 });
35603  /*
35604  * Based on:
35605  * Ext JS Library 1.1.1
35606  * Copyright(c) 2006-2007, Ext JS, LLC.
35607  *
35608  * Originally Released Under LGPL - original licence link has changed is not relivant.
35609  *
35610  * Fork - LGPL
35611  * <script type="text/javascript">
35612  */
35613  
35614 /**
35615  * @class Roo.bootstrap.layout.Basic
35616  * @extends Roo.util.Observable
35617  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35618  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35619  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35620  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35621  * @cfg {string}   region  the region that it inhabits..
35622  * @cfg {bool}   skipConfig skip config?
35623  * 
35624
35625  */
35626 Roo.bootstrap.layout.Basic = function(config){
35627     
35628     this.mgr = config.mgr;
35629     
35630     this.position = config.region;
35631     
35632     var skipConfig = config.skipConfig;
35633     
35634     this.events = {
35635         /**
35636          * @scope Roo.BasicLayoutRegion
35637          */
35638         
35639         /**
35640          * @event beforeremove
35641          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35642          * @param {Roo.LayoutRegion} this
35643          * @param {Roo.ContentPanel} panel The panel
35644          * @param {Object} e The cancel event object
35645          */
35646         "beforeremove" : true,
35647         /**
35648          * @event invalidated
35649          * Fires when the layout for this region is changed.
35650          * @param {Roo.LayoutRegion} this
35651          */
35652         "invalidated" : true,
35653         /**
35654          * @event visibilitychange
35655          * Fires when this region is shown or hidden 
35656          * @param {Roo.LayoutRegion} this
35657          * @param {Boolean} visibility true or false
35658          */
35659         "visibilitychange" : true,
35660         /**
35661          * @event paneladded
35662          * Fires when a panel is added. 
35663          * @param {Roo.LayoutRegion} this
35664          * @param {Roo.ContentPanel} panel The panel
35665          */
35666         "paneladded" : true,
35667         /**
35668          * @event panelremoved
35669          * Fires when a panel is removed. 
35670          * @param {Roo.LayoutRegion} this
35671          * @param {Roo.ContentPanel} panel The panel
35672          */
35673         "panelremoved" : true,
35674         /**
35675          * @event beforecollapse
35676          * Fires when this region before collapse.
35677          * @param {Roo.LayoutRegion} this
35678          */
35679         "beforecollapse" : true,
35680         /**
35681          * @event collapsed
35682          * Fires when this region is collapsed.
35683          * @param {Roo.LayoutRegion} this
35684          */
35685         "collapsed" : true,
35686         /**
35687          * @event expanded
35688          * Fires when this region is expanded.
35689          * @param {Roo.LayoutRegion} this
35690          */
35691         "expanded" : true,
35692         /**
35693          * @event slideshow
35694          * Fires when this region is slid into view.
35695          * @param {Roo.LayoutRegion} this
35696          */
35697         "slideshow" : true,
35698         /**
35699          * @event slidehide
35700          * Fires when this region slides out of view. 
35701          * @param {Roo.LayoutRegion} this
35702          */
35703         "slidehide" : true,
35704         /**
35705          * @event panelactivated
35706          * Fires when a panel is activated. 
35707          * @param {Roo.LayoutRegion} this
35708          * @param {Roo.ContentPanel} panel The activated panel
35709          */
35710         "panelactivated" : true,
35711         /**
35712          * @event resized
35713          * Fires when the user resizes this region. 
35714          * @param {Roo.LayoutRegion} this
35715          * @param {Number} newSize The new size (width for east/west, height for north/south)
35716          */
35717         "resized" : true
35718     };
35719     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35720     this.panels = new Roo.util.MixedCollection();
35721     this.panels.getKey = this.getPanelId.createDelegate(this);
35722     this.box = null;
35723     this.activePanel = null;
35724     // ensure listeners are added...
35725     
35726     if (config.listeners || config.events) {
35727         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35728             listeners : config.listeners || {},
35729             events : config.events || {}
35730         });
35731     }
35732     
35733     if(skipConfig !== true){
35734         this.applyConfig(config);
35735     }
35736 };
35737
35738 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35739 {
35740     getPanelId : function(p){
35741         return p.getId();
35742     },
35743     
35744     applyConfig : function(config){
35745         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35746         this.config = config;
35747         
35748     },
35749     
35750     /**
35751      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35752      * the width, for horizontal (north, south) the height.
35753      * @param {Number} newSize The new width or height
35754      */
35755     resizeTo : function(newSize){
35756         var el = this.el ? this.el :
35757                  (this.activePanel ? this.activePanel.getEl() : null);
35758         if(el){
35759             switch(this.position){
35760                 case "east":
35761                 case "west":
35762                     el.setWidth(newSize);
35763                     this.fireEvent("resized", this, newSize);
35764                 break;
35765                 case "north":
35766                 case "south":
35767                     el.setHeight(newSize);
35768                     this.fireEvent("resized", this, newSize);
35769                 break;                
35770             }
35771         }
35772     },
35773     
35774     getBox : function(){
35775         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35776     },
35777     
35778     getMargins : function(){
35779         return this.margins;
35780     },
35781     
35782     updateBox : function(box){
35783         this.box = box;
35784         var el = this.activePanel.getEl();
35785         el.dom.style.left = box.x + "px";
35786         el.dom.style.top = box.y + "px";
35787         this.activePanel.setSize(box.width, box.height);
35788     },
35789     
35790     /**
35791      * Returns the container element for this region.
35792      * @return {Roo.Element}
35793      */
35794     getEl : function(){
35795         return this.activePanel;
35796     },
35797     
35798     /**
35799      * Returns true if this region is currently visible.
35800      * @return {Boolean}
35801      */
35802     isVisible : function(){
35803         return this.activePanel ? true : false;
35804     },
35805     
35806     setActivePanel : function(panel){
35807         panel = this.getPanel(panel);
35808         if(this.activePanel && this.activePanel != panel){
35809             this.activePanel.setActiveState(false);
35810             this.activePanel.getEl().setLeftTop(-10000,-10000);
35811         }
35812         this.activePanel = panel;
35813         panel.setActiveState(true);
35814         if(this.box){
35815             panel.setSize(this.box.width, this.box.height);
35816         }
35817         this.fireEvent("panelactivated", this, panel);
35818         this.fireEvent("invalidated");
35819     },
35820     
35821     /**
35822      * Show the specified panel.
35823      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35824      * @return {Roo.ContentPanel} The shown panel or null
35825      */
35826     showPanel : function(panel){
35827         panel = this.getPanel(panel);
35828         if(panel){
35829             this.setActivePanel(panel);
35830         }
35831         return panel;
35832     },
35833     
35834     /**
35835      * Get the active panel for this region.
35836      * @return {Roo.ContentPanel} The active panel or null
35837      */
35838     getActivePanel : function(){
35839         return this.activePanel;
35840     },
35841     
35842     /**
35843      * Add the passed ContentPanel(s)
35844      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35845      * @return {Roo.ContentPanel} The panel added (if only one was added)
35846      */
35847     add : function(panel){
35848         if(arguments.length > 1){
35849             for(var i = 0, len = arguments.length; i < len; i++) {
35850                 this.add(arguments[i]);
35851             }
35852             return null;
35853         }
35854         if(this.hasPanel(panel)){
35855             this.showPanel(panel);
35856             return panel;
35857         }
35858         var el = panel.getEl();
35859         if(el.dom.parentNode != this.mgr.el.dom){
35860             this.mgr.el.dom.appendChild(el.dom);
35861         }
35862         if(panel.setRegion){
35863             panel.setRegion(this);
35864         }
35865         this.panels.add(panel);
35866         el.setStyle("position", "absolute");
35867         if(!panel.background){
35868             this.setActivePanel(panel);
35869             if(this.config.initialSize && this.panels.getCount()==1){
35870                 this.resizeTo(this.config.initialSize);
35871             }
35872         }
35873         this.fireEvent("paneladded", this, panel);
35874         return panel;
35875     },
35876     
35877     /**
35878      * Returns true if the panel is in this region.
35879      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35880      * @return {Boolean}
35881      */
35882     hasPanel : function(panel){
35883         if(typeof panel == "object"){ // must be panel obj
35884             panel = panel.getId();
35885         }
35886         return this.getPanel(panel) ? true : false;
35887     },
35888     
35889     /**
35890      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35891      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35892      * @param {Boolean} preservePanel Overrides the config preservePanel option
35893      * @return {Roo.ContentPanel} The panel that was removed
35894      */
35895     remove : function(panel, preservePanel){
35896         panel = this.getPanel(panel);
35897         if(!panel){
35898             return null;
35899         }
35900         var e = {};
35901         this.fireEvent("beforeremove", this, panel, e);
35902         if(e.cancel === true){
35903             return null;
35904         }
35905         var panelId = panel.getId();
35906         this.panels.removeKey(panelId);
35907         return panel;
35908     },
35909     
35910     /**
35911      * Returns the panel specified or null if it's not in this region.
35912      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35913      * @return {Roo.ContentPanel}
35914      */
35915     getPanel : function(id){
35916         if(typeof id == "object"){ // must be panel obj
35917             return id;
35918         }
35919         return this.panels.get(id);
35920     },
35921     
35922     /**
35923      * Returns this regions position (north/south/east/west/center).
35924      * @return {String} 
35925      */
35926     getPosition: function(){
35927         return this.position;    
35928     }
35929 });/*
35930  * Based on:
35931  * Ext JS Library 1.1.1
35932  * Copyright(c) 2006-2007, Ext JS, LLC.
35933  *
35934  * Originally Released Under LGPL - original licence link has changed is not relivant.
35935  *
35936  * Fork - LGPL
35937  * <script type="text/javascript">
35938  */
35939  
35940 /**
35941  * @class Roo.bootstrap.layout.Region
35942  * @extends Roo.bootstrap.layout.Basic
35943  * This class represents a region in a layout manager.
35944  
35945  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35946  * @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})
35947  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35948  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35949  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35950  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35951  * @cfg {String}    title           The title for the region (overrides panel titles)
35952  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35953  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35954  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35955  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35956  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35957  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35958  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35959  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35960  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35961  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35962
35963  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35964  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35965  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35966  * @cfg {Number}    width           For East/West panels
35967  * @cfg {Number}    height          For North/South panels
35968  * @cfg {Boolean}   split           To show the splitter
35969  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35970  * 
35971  * @cfg {string}   cls             Extra CSS classes to add to region
35972  * 
35973  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35974  * @cfg {string}   region  the region that it inhabits..
35975  *
35976
35977  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35978  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35979
35980  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35981  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35982  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35983  */
35984 Roo.bootstrap.layout.Region = function(config)
35985 {
35986     this.applyConfig(config);
35987
35988     var mgr = config.mgr;
35989     var pos = config.region;
35990     config.skipConfig = true;
35991     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35992     
35993     if (mgr.el) {
35994         this.onRender(mgr.el);   
35995     }
35996      
35997     this.visible = true;
35998     this.collapsed = false;
35999     this.unrendered_panels = [];
36000 };
36001
36002 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36003
36004     position: '', // set by wrapper (eg. north/south etc..)
36005     unrendered_panels : null,  // unrendered panels.
36006     
36007     tabPosition : false,
36008     
36009     mgr: false, // points to 'Border'
36010     
36011     
36012     createBody : function(){
36013         /** This region's body element 
36014         * @type Roo.Element */
36015         this.bodyEl = this.el.createChild({
36016                 tag: "div",
36017                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36018         });
36019     },
36020
36021     onRender: function(ctr, pos)
36022     {
36023         var dh = Roo.DomHelper;
36024         /** This region's container element 
36025         * @type Roo.Element */
36026         this.el = dh.append(ctr.dom, {
36027                 tag: "div",
36028                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36029             }, true);
36030         /** This region's title element 
36031         * @type Roo.Element */
36032     
36033         this.titleEl = dh.append(this.el.dom,  {
36034                 tag: "div",
36035                 unselectable: "on",
36036                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36037                 children:[
36038                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
36039                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36040                 ]
36041             }, true);
36042         
36043         this.titleEl.enableDisplayMode();
36044         /** This region's title text element 
36045         * @type HTMLElement */
36046         this.titleTextEl = this.titleEl.dom.firstChild;
36047         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36048         /*
36049         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36050         this.closeBtn.enableDisplayMode();
36051         this.closeBtn.on("click", this.closeClicked, this);
36052         this.closeBtn.hide();
36053     */
36054         this.createBody(this.config);
36055         if(this.config.hideWhenEmpty){
36056             this.hide();
36057             this.on("paneladded", this.validateVisibility, this);
36058             this.on("panelremoved", this.validateVisibility, this);
36059         }
36060         if(this.autoScroll){
36061             this.bodyEl.setStyle("overflow", "auto");
36062         }else{
36063             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36064         }
36065         //if(c.titlebar !== false){
36066             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36067                 this.titleEl.hide();
36068             }else{
36069                 this.titleEl.show();
36070                 if(this.config.title){
36071                     this.titleTextEl.innerHTML = this.config.title;
36072                 }
36073             }
36074         //}
36075         if(this.config.collapsed){
36076             this.collapse(true);
36077         }
36078         if(this.config.hidden){
36079             this.hide();
36080         }
36081         
36082         if (this.unrendered_panels && this.unrendered_panels.length) {
36083             for (var i =0;i< this.unrendered_panels.length; i++) {
36084                 this.add(this.unrendered_panels[i]);
36085             }
36086             this.unrendered_panels = null;
36087             
36088         }
36089         
36090     },
36091     
36092     applyConfig : function(c)
36093     {
36094         /*
36095          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36096             var dh = Roo.DomHelper;
36097             if(c.titlebar !== false){
36098                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36099                 this.collapseBtn.on("click", this.collapse, this);
36100                 this.collapseBtn.enableDisplayMode();
36101                 /*
36102                 if(c.showPin === true || this.showPin){
36103                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36104                     this.stickBtn.enableDisplayMode();
36105                     this.stickBtn.on("click", this.expand, this);
36106                     this.stickBtn.hide();
36107                 }
36108                 
36109             }
36110             */
36111             /** This region's collapsed element
36112             * @type Roo.Element */
36113             /*
36114              *
36115             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36116                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36117             ]}, true);
36118             
36119             if(c.floatable !== false){
36120                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36121                this.collapsedEl.on("click", this.collapseClick, this);
36122             }
36123
36124             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36125                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36126                    id: "message", unselectable: "on", style:{"float":"left"}});
36127                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36128              }
36129             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36130             this.expandBtn.on("click", this.expand, this);
36131             
36132         }
36133         
36134         if(this.collapseBtn){
36135             this.collapseBtn.setVisible(c.collapsible == true);
36136         }
36137         
36138         this.cmargins = c.cmargins || this.cmargins ||
36139                          (this.position == "west" || this.position == "east" ?
36140                              {top: 0, left: 2, right:2, bottom: 0} :
36141                              {top: 2, left: 0, right:0, bottom: 2});
36142         */
36143         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36144         
36145         
36146         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36147         
36148         this.autoScroll = c.autoScroll || false;
36149         
36150         
36151        
36152         
36153         this.duration = c.duration || .30;
36154         this.slideDuration = c.slideDuration || .45;
36155         this.config = c;
36156        
36157     },
36158     /**
36159      * Returns true if this region is currently visible.
36160      * @return {Boolean}
36161      */
36162     isVisible : function(){
36163         return this.visible;
36164     },
36165
36166     /**
36167      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36168      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36169      */
36170     //setCollapsedTitle : function(title){
36171     //    title = title || "&#160;";
36172      //   if(this.collapsedTitleTextEl){
36173       //      this.collapsedTitleTextEl.innerHTML = title;
36174        // }
36175     //},
36176
36177     getBox : function(){
36178         var b;
36179       //  if(!this.collapsed){
36180             b = this.el.getBox(false, true);
36181        // }else{
36182           //  b = this.collapsedEl.getBox(false, true);
36183         //}
36184         return b;
36185     },
36186
36187     getMargins : function(){
36188         return this.margins;
36189         //return this.collapsed ? this.cmargins : this.margins;
36190     },
36191 /*
36192     highlight : function(){
36193         this.el.addClass("x-layout-panel-dragover");
36194     },
36195
36196     unhighlight : function(){
36197         this.el.removeClass("x-layout-panel-dragover");
36198     },
36199 */
36200     updateBox : function(box)
36201     {
36202         if (!this.bodyEl) {
36203             return; // not rendered yet..
36204         }
36205         
36206         this.box = box;
36207         if(!this.collapsed){
36208             this.el.dom.style.left = box.x + "px";
36209             this.el.dom.style.top = box.y + "px";
36210             this.updateBody(box.width, box.height);
36211         }else{
36212             this.collapsedEl.dom.style.left = box.x + "px";
36213             this.collapsedEl.dom.style.top = box.y + "px";
36214             this.collapsedEl.setSize(box.width, box.height);
36215         }
36216         if(this.tabs){
36217             this.tabs.autoSizeTabs();
36218         }
36219     },
36220
36221     updateBody : function(w, h)
36222     {
36223         if(w !== null){
36224             this.el.setWidth(w);
36225             w -= this.el.getBorderWidth("rl");
36226             if(this.config.adjustments){
36227                 w += this.config.adjustments[0];
36228             }
36229         }
36230         if(h !== null && h > 0){
36231             this.el.setHeight(h);
36232             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36233             h -= this.el.getBorderWidth("tb");
36234             if(this.config.adjustments){
36235                 h += this.config.adjustments[1];
36236             }
36237             this.bodyEl.setHeight(h);
36238             if(this.tabs){
36239                 h = this.tabs.syncHeight(h);
36240             }
36241         }
36242         if(this.panelSize){
36243             w = w !== null ? w : this.panelSize.width;
36244             h = h !== null ? h : this.panelSize.height;
36245         }
36246         if(this.activePanel){
36247             var el = this.activePanel.getEl();
36248             w = w !== null ? w : el.getWidth();
36249             h = h !== null ? h : el.getHeight();
36250             this.panelSize = {width: w, height: h};
36251             this.activePanel.setSize(w, h);
36252         }
36253         if(Roo.isIE && this.tabs){
36254             this.tabs.el.repaint();
36255         }
36256     },
36257
36258     /**
36259      * Returns the container element for this region.
36260      * @return {Roo.Element}
36261      */
36262     getEl : function(){
36263         return this.el;
36264     },
36265
36266     /**
36267      * Hides this region.
36268      */
36269     hide : function(){
36270         //if(!this.collapsed){
36271             this.el.dom.style.left = "-2000px";
36272             this.el.hide();
36273         //}else{
36274          //   this.collapsedEl.dom.style.left = "-2000px";
36275          //   this.collapsedEl.hide();
36276        // }
36277         this.visible = false;
36278         this.fireEvent("visibilitychange", this, false);
36279     },
36280
36281     /**
36282      * Shows this region if it was previously hidden.
36283      */
36284     show : function(){
36285         //if(!this.collapsed){
36286             this.el.show();
36287         //}else{
36288         //    this.collapsedEl.show();
36289        // }
36290         this.visible = true;
36291         this.fireEvent("visibilitychange", this, true);
36292     },
36293 /*
36294     closeClicked : function(){
36295         if(this.activePanel){
36296             this.remove(this.activePanel);
36297         }
36298     },
36299
36300     collapseClick : function(e){
36301         if(this.isSlid){
36302            e.stopPropagation();
36303            this.slideIn();
36304         }else{
36305            e.stopPropagation();
36306            this.slideOut();
36307         }
36308     },
36309 */
36310     /**
36311      * Collapses this region.
36312      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36313      */
36314     /*
36315     collapse : function(skipAnim, skipCheck = false){
36316         if(this.collapsed) {
36317             return;
36318         }
36319         
36320         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36321             
36322             this.collapsed = true;
36323             if(this.split){
36324                 this.split.el.hide();
36325             }
36326             if(this.config.animate && skipAnim !== true){
36327                 this.fireEvent("invalidated", this);
36328                 this.animateCollapse();
36329             }else{
36330                 this.el.setLocation(-20000,-20000);
36331                 this.el.hide();
36332                 this.collapsedEl.show();
36333                 this.fireEvent("collapsed", this);
36334                 this.fireEvent("invalidated", this);
36335             }
36336         }
36337         
36338     },
36339 */
36340     animateCollapse : function(){
36341         // overridden
36342     },
36343
36344     /**
36345      * Expands this region if it was previously collapsed.
36346      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36347      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36348      */
36349     /*
36350     expand : function(e, skipAnim){
36351         if(e) {
36352             e.stopPropagation();
36353         }
36354         if(!this.collapsed || this.el.hasActiveFx()) {
36355             return;
36356         }
36357         if(this.isSlid){
36358             this.afterSlideIn();
36359             skipAnim = true;
36360         }
36361         this.collapsed = false;
36362         if(this.config.animate && skipAnim !== true){
36363             this.animateExpand();
36364         }else{
36365             this.el.show();
36366             if(this.split){
36367                 this.split.el.show();
36368             }
36369             this.collapsedEl.setLocation(-2000,-2000);
36370             this.collapsedEl.hide();
36371             this.fireEvent("invalidated", this);
36372             this.fireEvent("expanded", this);
36373         }
36374     },
36375 */
36376     animateExpand : function(){
36377         // overridden
36378     },
36379
36380     initTabs : function()
36381     {
36382         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36383         
36384         var ts = new Roo.bootstrap.panel.Tabs({
36385             el: this.bodyEl.dom,
36386             region : this,
36387             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36388             disableTooltips: this.config.disableTabTips,
36389             toolbar : this.config.toolbar
36390         });
36391         
36392         if(this.config.hideTabs){
36393             ts.stripWrap.setDisplayed(false);
36394         }
36395         this.tabs = ts;
36396         ts.resizeTabs = this.config.resizeTabs === true;
36397         ts.minTabWidth = this.config.minTabWidth || 40;
36398         ts.maxTabWidth = this.config.maxTabWidth || 250;
36399         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36400         ts.monitorResize = false;
36401         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36402         ts.bodyEl.addClass('roo-layout-tabs-body');
36403         this.panels.each(this.initPanelAsTab, this);
36404     },
36405
36406     initPanelAsTab : function(panel){
36407         var ti = this.tabs.addTab(
36408             panel.getEl().id,
36409             panel.getTitle(),
36410             null,
36411             this.config.closeOnTab && panel.isClosable(),
36412             panel.tpl
36413         );
36414         if(panel.tabTip !== undefined){
36415             ti.setTooltip(panel.tabTip);
36416         }
36417         ti.on("activate", function(){
36418               this.setActivePanel(panel);
36419         }, this);
36420         
36421         if(this.config.closeOnTab){
36422             ti.on("beforeclose", function(t, e){
36423                 e.cancel = true;
36424                 this.remove(panel);
36425             }, this);
36426         }
36427         
36428         panel.tabItem = ti;
36429         
36430         return ti;
36431     },
36432
36433     updatePanelTitle : function(panel, title)
36434     {
36435         if(this.activePanel == panel){
36436             this.updateTitle(title);
36437         }
36438         if(this.tabs){
36439             var ti = this.tabs.getTab(panel.getEl().id);
36440             ti.setText(title);
36441             if(panel.tabTip !== undefined){
36442                 ti.setTooltip(panel.tabTip);
36443             }
36444         }
36445     },
36446
36447     updateTitle : function(title){
36448         if(this.titleTextEl && !this.config.title){
36449             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36450         }
36451     },
36452
36453     setActivePanel : function(panel)
36454     {
36455         panel = this.getPanel(panel);
36456         if(this.activePanel && this.activePanel != panel){
36457             if(this.activePanel.setActiveState(false) === false){
36458                 return;
36459             }
36460         }
36461         this.activePanel = panel;
36462         panel.setActiveState(true);
36463         if(this.panelSize){
36464             panel.setSize(this.panelSize.width, this.panelSize.height);
36465         }
36466         if(this.closeBtn){
36467             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36468         }
36469         this.updateTitle(panel.getTitle());
36470         if(this.tabs){
36471             this.fireEvent("invalidated", this);
36472         }
36473         this.fireEvent("panelactivated", this, panel);
36474     },
36475
36476     /**
36477      * Shows the specified panel.
36478      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36479      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36480      */
36481     showPanel : function(panel)
36482     {
36483         panel = this.getPanel(panel);
36484         if(panel){
36485             if(this.tabs){
36486                 var tab = this.tabs.getTab(panel.getEl().id);
36487                 if(tab.isHidden()){
36488                     this.tabs.unhideTab(tab.id);
36489                 }
36490                 tab.activate();
36491             }else{
36492                 this.setActivePanel(panel);
36493             }
36494         }
36495         return panel;
36496     },
36497
36498     /**
36499      * Get the active panel for this region.
36500      * @return {Roo.ContentPanel} The active panel or null
36501      */
36502     getActivePanel : function(){
36503         return this.activePanel;
36504     },
36505
36506     validateVisibility : function(){
36507         if(this.panels.getCount() < 1){
36508             this.updateTitle("&#160;");
36509             this.closeBtn.hide();
36510             this.hide();
36511         }else{
36512             if(!this.isVisible()){
36513                 this.show();
36514             }
36515         }
36516     },
36517
36518     /**
36519      * Adds the passed ContentPanel(s) to this region.
36520      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36521      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36522      */
36523     add : function(panel)
36524     {
36525         if(arguments.length > 1){
36526             for(var i = 0, len = arguments.length; i < len; i++) {
36527                 this.add(arguments[i]);
36528             }
36529             return null;
36530         }
36531         
36532         // if we have not been rendered yet, then we can not really do much of this..
36533         if (!this.bodyEl) {
36534             this.unrendered_panels.push(panel);
36535             return panel;
36536         }
36537         
36538         
36539         
36540         
36541         if(this.hasPanel(panel)){
36542             this.showPanel(panel);
36543             return panel;
36544         }
36545         panel.setRegion(this);
36546         this.panels.add(panel);
36547        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36548             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36549             // and hide them... ???
36550             this.bodyEl.dom.appendChild(panel.getEl().dom);
36551             if(panel.background !== true){
36552                 this.setActivePanel(panel);
36553             }
36554             this.fireEvent("paneladded", this, panel);
36555             return panel;
36556         }
36557         */
36558         if(!this.tabs){
36559             this.initTabs();
36560         }else{
36561             this.initPanelAsTab(panel);
36562         }
36563         
36564         
36565         if(panel.background !== true){
36566             this.tabs.activate(panel.getEl().id);
36567         }
36568         this.fireEvent("paneladded", this, panel);
36569         return panel;
36570     },
36571
36572     /**
36573      * Hides the tab for the specified panel.
36574      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36575      */
36576     hidePanel : function(panel){
36577         if(this.tabs && (panel = this.getPanel(panel))){
36578             this.tabs.hideTab(panel.getEl().id);
36579         }
36580     },
36581
36582     /**
36583      * Unhides the tab for a previously hidden panel.
36584      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36585      */
36586     unhidePanel : function(panel){
36587         if(this.tabs && (panel = this.getPanel(panel))){
36588             this.tabs.unhideTab(panel.getEl().id);
36589         }
36590     },
36591
36592     clearPanels : function(){
36593         while(this.panels.getCount() > 0){
36594              this.remove(this.panels.first());
36595         }
36596     },
36597
36598     /**
36599      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36600      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36601      * @param {Boolean} preservePanel Overrides the config preservePanel option
36602      * @return {Roo.ContentPanel} The panel that was removed
36603      */
36604     remove : function(panel, preservePanel)
36605     {
36606         panel = this.getPanel(panel);
36607         if(!panel){
36608             return null;
36609         }
36610         var e = {};
36611         this.fireEvent("beforeremove", this, panel, e);
36612         if(e.cancel === true){
36613             return null;
36614         }
36615         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36616         var panelId = panel.getId();
36617         this.panels.removeKey(panelId);
36618         if(preservePanel){
36619             document.body.appendChild(panel.getEl().dom);
36620         }
36621         if(this.tabs){
36622             this.tabs.removeTab(panel.getEl().id);
36623         }else if (!preservePanel){
36624             this.bodyEl.dom.removeChild(panel.getEl().dom);
36625         }
36626         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36627             var p = this.panels.first();
36628             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36629             tempEl.appendChild(p.getEl().dom);
36630             this.bodyEl.update("");
36631             this.bodyEl.dom.appendChild(p.getEl().dom);
36632             tempEl = null;
36633             this.updateTitle(p.getTitle());
36634             this.tabs = null;
36635             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36636             this.setActivePanel(p);
36637         }
36638         panel.setRegion(null);
36639         if(this.activePanel == panel){
36640             this.activePanel = null;
36641         }
36642         if(this.config.autoDestroy !== false && preservePanel !== true){
36643             try{panel.destroy();}catch(e){}
36644         }
36645         this.fireEvent("panelremoved", this, panel);
36646         return panel;
36647     },
36648
36649     /**
36650      * Returns the TabPanel component used by this region
36651      * @return {Roo.TabPanel}
36652      */
36653     getTabs : function(){
36654         return this.tabs;
36655     },
36656
36657     createTool : function(parentEl, className){
36658         var btn = Roo.DomHelper.append(parentEl, {
36659             tag: "div",
36660             cls: "x-layout-tools-button",
36661             children: [ {
36662                 tag: "div",
36663                 cls: "roo-layout-tools-button-inner " + className,
36664                 html: "&#160;"
36665             }]
36666         }, true);
36667         btn.addClassOnOver("roo-layout-tools-button-over");
36668         return btn;
36669     }
36670 });/*
36671  * Based on:
36672  * Ext JS Library 1.1.1
36673  * Copyright(c) 2006-2007, Ext JS, LLC.
36674  *
36675  * Originally Released Under LGPL - original licence link has changed is not relivant.
36676  *
36677  * Fork - LGPL
36678  * <script type="text/javascript">
36679  */
36680  
36681
36682
36683 /**
36684  * @class Roo.SplitLayoutRegion
36685  * @extends Roo.LayoutRegion
36686  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36687  */
36688 Roo.bootstrap.layout.Split = function(config){
36689     this.cursor = config.cursor;
36690     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36691 };
36692
36693 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36694 {
36695     splitTip : "Drag to resize.",
36696     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36697     useSplitTips : false,
36698
36699     applyConfig : function(config){
36700         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36701     },
36702     
36703     onRender : function(ctr,pos) {
36704         
36705         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36706         if(!this.config.split){
36707             return;
36708         }
36709         if(!this.split){
36710             
36711             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36712                             tag: "div",
36713                             id: this.el.id + "-split",
36714                             cls: "roo-layout-split roo-layout-split-"+this.position,
36715                             html: "&#160;"
36716             });
36717             /** The SplitBar for this region 
36718             * @type Roo.SplitBar */
36719             // does not exist yet...
36720             Roo.log([this.position, this.orientation]);
36721             
36722             this.split = new Roo.bootstrap.SplitBar({
36723                 dragElement : splitEl,
36724                 resizingElement: this.el,
36725                 orientation : this.orientation
36726             });
36727             
36728             this.split.on("moved", this.onSplitMove, this);
36729             this.split.useShim = this.config.useShim === true;
36730             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36731             if(this.useSplitTips){
36732                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36733             }
36734             //if(config.collapsible){
36735             //    this.split.el.on("dblclick", this.collapse,  this);
36736             //}
36737         }
36738         if(typeof this.config.minSize != "undefined"){
36739             this.split.minSize = this.config.minSize;
36740         }
36741         if(typeof this.config.maxSize != "undefined"){
36742             this.split.maxSize = this.config.maxSize;
36743         }
36744         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36745             this.hideSplitter();
36746         }
36747         
36748     },
36749
36750     getHMaxSize : function(){
36751          var cmax = this.config.maxSize || 10000;
36752          var center = this.mgr.getRegion("center");
36753          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36754     },
36755
36756     getVMaxSize : function(){
36757          var cmax = this.config.maxSize || 10000;
36758          var center = this.mgr.getRegion("center");
36759          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36760     },
36761
36762     onSplitMove : function(split, newSize){
36763         this.fireEvent("resized", this, newSize);
36764     },
36765     
36766     /** 
36767      * Returns the {@link Roo.SplitBar} for this region.
36768      * @return {Roo.SplitBar}
36769      */
36770     getSplitBar : function(){
36771         return this.split;
36772     },
36773     
36774     hide : function(){
36775         this.hideSplitter();
36776         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36777     },
36778
36779     hideSplitter : function(){
36780         if(this.split){
36781             this.split.el.setLocation(-2000,-2000);
36782             this.split.el.hide();
36783         }
36784     },
36785
36786     show : function(){
36787         if(this.split){
36788             this.split.el.show();
36789         }
36790         Roo.bootstrap.layout.Split.superclass.show.call(this);
36791     },
36792     
36793     beforeSlide: function(){
36794         if(Roo.isGecko){// firefox overflow auto bug workaround
36795             this.bodyEl.clip();
36796             if(this.tabs) {
36797                 this.tabs.bodyEl.clip();
36798             }
36799             if(this.activePanel){
36800                 this.activePanel.getEl().clip();
36801                 
36802                 if(this.activePanel.beforeSlide){
36803                     this.activePanel.beforeSlide();
36804                 }
36805             }
36806         }
36807     },
36808     
36809     afterSlide : function(){
36810         if(Roo.isGecko){// firefox overflow auto bug workaround
36811             this.bodyEl.unclip();
36812             if(this.tabs) {
36813                 this.tabs.bodyEl.unclip();
36814             }
36815             if(this.activePanel){
36816                 this.activePanel.getEl().unclip();
36817                 if(this.activePanel.afterSlide){
36818                     this.activePanel.afterSlide();
36819                 }
36820             }
36821         }
36822     },
36823
36824     initAutoHide : function(){
36825         if(this.autoHide !== false){
36826             if(!this.autoHideHd){
36827                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36828                 this.autoHideHd = {
36829                     "mouseout": function(e){
36830                         if(!e.within(this.el, true)){
36831                             st.delay(500);
36832                         }
36833                     },
36834                     "mouseover" : function(e){
36835                         st.cancel();
36836                     },
36837                     scope : this
36838                 };
36839             }
36840             this.el.on(this.autoHideHd);
36841         }
36842     },
36843
36844     clearAutoHide : function(){
36845         if(this.autoHide !== false){
36846             this.el.un("mouseout", this.autoHideHd.mouseout);
36847             this.el.un("mouseover", this.autoHideHd.mouseover);
36848         }
36849     },
36850
36851     clearMonitor : function(){
36852         Roo.get(document).un("click", this.slideInIf, this);
36853     },
36854
36855     // these names are backwards but not changed for compat
36856     slideOut : function(){
36857         if(this.isSlid || this.el.hasActiveFx()){
36858             return;
36859         }
36860         this.isSlid = true;
36861         if(this.collapseBtn){
36862             this.collapseBtn.hide();
36863         }
36864         this.closeBtnState = this.closeBtn.getStyle('display');
36865         this.closeBtn.hide();
36866         if(this.stickBtn){
36867             this.stickBtn.show();
36868         }
36869         this.el.show();
36870         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36871         this.beforeSlide();
36872         this.el.setStyle("z-index", 10001);
36873         this.el.slideIn(this.getSlideAnchor(), {
36874             callback: function(){
36875                 this.afterSlide();
36876                 this.initAutoHide();
36877                 Roo.get(document).on("click", this.slideInIf, this);
36878                 this.fireEvent("slideshow", this);
36879             },
36880             scope: this,
36881             block: true
36882         });
36883     },
36884
36885     afterSlideIn : function(){
36886         this.clearAutoHide();
36887         this.isSlid = false;
36888         this.clearMonitor();
36889         this.el.setStyle("z-index", "");
36890         if(this.collapseBtn){
36891             this.collapseBtn.show();
36892         }
36893         this.closeBtn.setStyle('display', this.closeBtnState);
36894         if(this.stickBtn){
36895             this.stickBtn.hide();
36896         }
36897         this.fireEvent("slidehide", this);
36898     },
36899
36900     slideIn : function(cb){
36901         if(!this.isSlid || this.el.hasActiveFx()){
36902             Roo.callback(cb);
36903             return;
36904         }
36905         this.isSlid = false;
36906         this.beforeSlide();
36907         this.el.slideOut(this.getSlideAnchor(), {
36908             callback: function(){
36909                 this.el.setLeftTop(-10000, -10000);
36910                 this.afterSlide();
36911                 this.afterSlideIn();
36912                 Roo.callback(cb);
36913             },
36914             scope: this,
36915             block: true
36916         });
36917     },
36918     
36919     slideInIf : function(e){
36920         if(!e.within(this.el)){
36921             this.slideIn();
36922         }
36923     },
36924
36925     animateCollapse : function(){
36926         this.beforeSlide();
36927         this.el.setStyle("z-index", 20000);
36928         var anchor = this.getSlideAnchor();
36929         this.el.slideOut(anchor, {
36930             callback : function(){
36931                 this.el.setStyle("z-index", "");
36932                 this.collapsedEl.slideIn(anchor, {duration:.3});
36933                 this.afterSlide();
36934                 this.el.setLocation(-10000,-10000);
36935                 this.el.hide();
36936                 this.fireEvent("collapsed", this);
36937             },
36938             scope: this,
36939             block: true
36940         });
36941     },
36942
36943     animateExpand : function(){
36944         this.beforeSlide();
36945         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36946         this.el.setStyle("z-index", 20000);
36947         this.collapsedEl.hide({
36948             duration:.1
36949         });
36950         this.el.slideIn(this.getSlideAnchor(), {
36951             callback : function(){
36952                 this.el.setStyle("z-index", "");
36953                 this.afterSlide();
36954                 if(this.split){
36955                     this.split.el.show();
36956                 }
36957                 this.fireEvent("invalidated", this);
36958                 this.fireEvent("expanded", this);
36959             },
36960             scope: this,
36961             block: true
36962         });
36963     },
36964
36965     anchors : {
36966         "west" : "left",
36967         "east" : "right",
36968         "north" : "top",
36969         "south" : "bottom"
36970     },
36971
36972     sanchors : {
36973         "west" : "l",
36974         "east" : "r",
36975         "north" : "t",
36976         "south" : "b"
36977     },
36978
36979     canchors : {
36980         "west" : "tl-tr",
36981         "east" : "tr-tl",
36982         "north" : "tl-bl",
36983         "south" : "bl-tl"
36984     },
36985
36986     getAnchor : function(){
36987         return this.anchors[this.position];
36988     },
36989
36990     getCollapseAnchor : function(){
36991         return this.canchors[this.position];
36992     },
36993
36994     getSlideAnchor : function(){
36995         return this.sanchors[this.position];
36996     },
36997
36998     getAlignAdj : function(){
36999         var cm = this.cmargins;
37000         switch(this.position){
37001             case "west":
37002                 return [0, 0];
37003             break;
37004             case "east":
37005                 return [0, 0];
37006             break;
37007             case "north":
37008                 return [0, 0];
37009             break;
37010             case "south":
37011                 return [0, 0];
37012             break;
37013         }
37014     },
37015
37016     getExpandAdj : function(){
37017         var c = this.collapsedEl, cm = this.cmargins;
37018         switch(this.position){
37019             case "west":
37020                 return [-(cm.right+c.getWidth()+cm.left), 0];
37021             break;
37022             case "east":
37023                 return [cm.right+c.getWidth()+cm.left, 0];
37024             break;
37025             case "north":
37026                 return [0, -(cm.top+cm.bottom+c.getHeight())];
37027             break;
37028             case "south":
37029                 return [0, cm.top+cm.bottom+c.getHeight()];
37030             break;
37031         }
37032     }
37033 });/*
37034  * Based on:
37035  * Ext JS Library 1.1.1
37036  * Copyright(c) 2006-2007, Ext JS, LLC.
37037  *
37038  * Originally Released Under LGPL - original licence link has changed is not relivant.
37039  *
37040  * Fork - LGPL
37041  * <script type="text/javascript">
37042  */
37043 /*
37044  * These classes are private internal classes
37045  */
37046 Roo.bootstrap.layout.Center = function(config){
37047     config.region = "center";
37048     Roo.bootstrap.layout.Region.call(this, config);
37049     this.visible = true;
37050     this.minWidth = config.minWidth || 20;
37051     this.minHeight = config.minHeight || 20;
37052 };
37053
37054 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37055     hide : function(){
37056         // center panel can't be hidden
37057     },
37058     
37059     show : function(){
37060         // center panel can't be hidden
37061     },
37062     
37063     getMinWidth: function(){
37064         return this.minWidth;
37065     },
37066     
37067     getMinHeight: function(){
37068         return this.minHeight;
37069     }
37070 });
37071
37072
37073
37074
37075  
37076
37077
37078
37079
37080
37081
37082 Roo.bootstrap.layout.North = function(config)
37083 {
37084     config.region = 'north';
37085     config.cursor = 'n-resize';
37086     
37087     Roo.bootstrap.layout.Split.call(this, config);
37088     
37089     
37090     if(this.split){
37091         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37092         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37093         this.split.el.addClass("roo-layout-split-v");
37094     }
37095     var size = config.initialSize || config.height;
37096     if(typeof size != "undefined"){
37097         this.el.setHeight(size);
37098     }
37099 };
37100 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37101 {
37102     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37103     
37104     
37105     
37106     getBox : function(){
37107         if(this.collapsed){
37108             return this.collapsedEl.getBox();
37109         }
37110         var box = this.el.getBox();
37111         if(this.split){
37112             box.height += this.split.el.getHeight();
37113         }
37114         return box;
37115     },
37116     
37117     updateBox : function(box){
37118         if(this.split && !this.collapsed){
37119             box.height -= this.split.el.getHeight();
37120             this.split.el.setLeft(box.x);
37121             this.split.el.setTop(box.y+box.height);
37122             this.split.el.setWidth(box.width);
37123         }
37124         if(this.collapsed){
37125             this.updateBody(box.width, null);
37126         }
37127         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37128     }
37129 });
37130
37131
37132
37133
37134
37135 Roo.bootstrap.layout.South = function(config){
37136     config.region = 'south';
37137     config.cursor = 's-resize';
37138     Roo.bootstrap.layout.Split.call(this, config);
37139     if(this.split){
37140         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37141         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37142         this.split.el.addClass("roo-layout-split-v");
37143     }
37144     var size = config.initialSize || config.height;
37145     if(typeof size != "undefined"){
37146         this.el.setHeight(size);
37147     }
37148 };
37149
37150 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37151     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37152     getBox : function(){
37153         if(this.collapsed){
37154             return this.collapsedEl.getBox();
37155         }
37156         var box = this.el.getBox();
37157         if(this.split){
37158             var sh = this.split.el.getHeight();
37159             box.height += sh;
37160             box.y -= sh;
37161         }
37162         return box;
37163     },
37164     
37165     updateBox : function(box){
37166         if(this.split && !this.collapsed){
37167             var sh = this.split.el.getHeight();
37168             box.height -= sh;
37169             box.y += sh;
37170             this.split.el.setLeft(box.x);
37171             this.split.el.setTop(box.y-sh);
37172             this.split.el.setWidth(box.width);
37173         }
37174         if(this.collapsed){
37175             this.updateBody(box.width, null);
37176         }
37177         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37178     }
37179 });
37180
37181 Roo.bootstrap.layout.East = function(config){
37182     config.region = "east";
37183     config.cursor = "e-resize";
37184     Roo.bootstrap.layout.Split.call(this, config);
37185     if(this.split){
37186         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37187         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37188         this.split.el.addClass("roo-layout-split-h");
37189     }
37190     var size = config.initialSize || config.width;
37191     if(typeof size != "undefined"){
37192         this.el.setWidth(size);
37193     }
37194 };
37195 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37196     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37197     getBox : function(){
37198         if(this.collapsed){
37199             return this.collapsedEl.getBox();
37200         }
37201         var box = this.el.getBox();
37202         if(this.split){
37203             var sw = this.split.el.getWidth();
37204             box.width += sw;
37205             box.x -= sw;
37206         }
37207         return box;
37208     },
37209
37210     updateBox : function(box){
37211         if(this.split && !this.collapsed){
37212             var sw = this.split.el.getWidth();
37213             box.width -= sw;
37214             this.split.el.setLeft(box.x);
37215             this.split.el.setTop(box.y);
37216             this.split.el.setHeight(box.height);
37217             box.x += sw;
37218         }
37219         if(this.collapsed){
37220             this.updateBody(null, box.height);
37221         }
37222         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37223     }
37224 });
37225
37226 Roo.bootstrap.layout.West = function(config){
37227     config.region = "west";
37228     config.cursor = "w-resize";
37229     
37230     Roo.bootstrap.layout.Split.call(this, config);
37231     if(this.split){
37232         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37233         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37234         this.split.el.addClass("roo-layout-split-h");
37235     }
37236     
37237 };
37238 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37239     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37240     
37241     onRender: function(ctr, pos)
37242     {
37243         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37244         var size = this.config.initialSize || this.config.width;
37245         if(typeof size != "undefined"){
37246             this.el.setWidth(size);
37247         }
37248     },
37249     
37250     getBox : function(){
37251         if(this.collapsed){
37252             return this.collapsedEl.getBox();
37253         }
37254         var box = this.el.getBox();
37255         if(this.split){
37256             box.width += this.split.el.getWidth();
37257         }
37258         return box;
37259     },
37260     
37261     updateBox : function(box){
37262         if(this.split && !this.collapsed){
37263             var sw = this.split.el.getWidth();
37264             box.width -= sw;
37265             this.split.el.setLeft(box.x+box.width);
37266             this.split.el.setTop(box.y);
37267             this.split.el.setHeight(box.height);
37268         }
37269         if(this.collapsed){
37270             this.updateBody(null, box.height);
37271         }
37272         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37273     }
37274 });Roo.namespace("Roo.bootstrap.panel");/*
37275  * Based on:
37276  * Ext JS Library 1.1.1
37277  * Copyright(c) 2006-2007, Ext JS, LLC.
37278  *
37279  * Originally Released Under LGPL - original licence link has changed is not relivant.
37280  *
37281  * Fork - LGPL
37282  * <script type="text/javascript">
37283  */
37284 /**
37285  * @class Roo.ContentPanel
37286  * @extends Roo.util.Observable
37287  * A basic ContentPanel element.
37288  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37289  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37290  * @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
37291  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37292  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37293  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37294  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37295  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37296  * @cfg {String} title          The title for this panel
37297  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37298  * @cfg {String} url            Calls {@link #setUrl} with this value
37299  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37300  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37301  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37302  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37303  * @cfg {Boolean} badges render the badges
37304
37305  * @constructor
37306  * Create a new ContentPanel.
37307  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37308  * @param {String/Object} config A string to set only the title or a config object
37309  * @param {String} content (optional) Set the HTML content for this panel
37310  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37311  */
37312 Roo.bootstrap.panel.Content = function( config){
37313     
37314     this.tpl = config.tpl || false;
37315     
37316     var el = config.el;
37317     var content = config.content;
37318
37319     if(config.autoCreate){ // xtype is available if this is called from factory
37320         el = Roo.id();
37321     }
37322     this.el = Roo.get(el);
37323     if(!this.el && config && config.autoCreate){
37324         if(typeof config.autoCreate == "object"){
37325             if(!config.autoCreate.id){
37326                 config.autoCreate.id = config.id||el;
37327             }
37328             this.el = Roo.DomHelper.append(document.body,
37329                         config.autoCreate, true);
37330         }else{
37331             var elcfg =  {   tag: "div",
37332                             cls: "roo-layout-inactive-content",
37333                             id: config.id||el
37334                             };
37335             if (config.html) {
37336                 elcfg.html = config.html;
37337                 
37338             }
37339                         
37340             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37341         }
37342     } 
37343     this.closable = false;
37344     this.loaded = false;
37345     this.active = false;
37346    
37347       
37348     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37349         
37350         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37351         
37352         this.wrapEl = this.el; //this.el.wrap();
37353         var ti = [];
37354         if (config.toolbar.items) {
37355             ti = config.toolbar.items ;
37356             delete config.toolbar.items ;
37357         }
37358         
37359         var nitems = [];
37360         this.toolbar.render(this.wrapEl, 'before');
37361         for(var i =0;i < ti.length;i++) {
37362           //  Roo.log(['add child', items[i]]);
37363             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37364         }
37365         this.toolbar.items = nitems;
37366         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37367         delete config.toolbar;
37368         
37369     }
37370     /*
37371     // xtype created footer. - not sure if will work as we normally have to render first..
37372     if (this.footer && !this.footer.el && this.footer.xtype) {
37373         if (!this.wrapEl) {
37374             this.wrapEl = this.el.wrap();
37375         }
37376     
37377         this.footer.container = this.wrapEl.createChild();
37378          
37379         this.footer = Roo.factory(this.footer, Roo);
37380         
37381     }
37382     */
37383     
37384      if(typeof config == "string"){
37385         this.title = config;
37386     }else{
37387         Roo.apply(this, config);
37388     }
37389     
37390     if(this.resizeEl){
37391         this.resizeEl = Roo.get(this.resizeEl, true);
37392     }else{
37393         this.resizeEl = this.el;
37394     }
37395     // handle view.xtype
37396     
37397  
37398     
37399     
37400     this.addEvents({
37401         /**
37402          * @event activate
37403          * Fires when this panel is activated. 
37404          * @param {Roo.ContentPanel} this
37405          */
37406         "activate" : true,
37407         /**
37408          * @event deactivate
37409          * Fires when this panel is activated. 
37410          * @param {Roo.ContentPanel} this
37411          */
37412         "deactivate" : true,
37413
37414         /**
37415          * @event resize
37416          * Fires when this panel is resized if fitToFrame is true.
37417          * @param {Roo.ContentPanel} this
37418          * @param {Number} width The width after any component adjustments
37419          * @param {Number} height The height after any component adjustments
37420          */
37421         "resize" : true,
37422         
37423          /**
37424          * @event render
37425          * Fires when this tab is created
37426          * @param {Roo.ContentPanel} this
37427          */
37428         "render" : true
37429         
37430         
37431         
37432     });
37433     
37434
37435     
37436     
37437     if(this.autoScroll){
37438         this.resizeEl.setStyle("overflow", "auto");
37439     } else {
37440         // fix randome scrolling
37441         //this.el.on('scroll', function() {
37442         //    Roo.log('fix random scolling');
37443         //    this.scrollTo('top',0); 
37444         //});
37445     }
37446     content = content || this.content;
37447     if(content){
37448         this.setContent(content);
37449     }
37450     if(config && config.url){
37451         this.setUrl(this.url, this.params, this.loadOnce);
37452     }
37453     
37454     
37455     
37456     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37457     
37458     if (this.view && typeof(this.view.xtype) != 'undefined') {
37459         this.view.el = this.el.appendChild(document.createElement("div"));
37460         this.view = Roo.factory(this.view); 
37461         this.view.render  &&  this.view.render(false, '');  
37462     }
37463     
37464     
37465     this.fireEvent('render', this);
37466 };
37467
37468 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37469     
37470     tabTip : '',
37471     
37472     setRegion : function(region){
37473         this.region = region;
37474         this.setActiveClass(region && !this.background);
37475     },
37476     
37477     
37478     setActiveClass: function(state)
37479     {
37480         if(state){
37481            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37482            this.el.setStyle('position','relative');
37483         }else{
37484            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37485            this.el.setStyle('position', 'absolute');
37486         } 
37487     },
37488     
37489     /**
37490      * Returns the toolbar for this Panel if one was configured. 
37491      * @return {Roo.Toolbar} 
37492      */
37493     getToolbar : function(){
37494         return this.toolbar;
37495     },
37496     
37497     setActiveState : function(active)
37498     {
37499         this.active = active;
37500         this.setActiveClass(active);
37501         if(!active){
37502             if(this.fireEvent("deactivate", this) === false){
37503                 return false;
37504             }
37505             return true;
37506         }
37507         this.fireEvent("activate", this);
37508         return true;
37509     },
37510     /**
37511      * Updates this panel's element
37512      * @param {String} content The new content
37513      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37514     */
37515     setContent : function(content, loadScripts){
37516         this.el.update(content, loadScripts);
37517     },
37518
37519     ignoreResize : function(w, h){
37520         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37521             return true;
37522         }else{
37523             this.lastSize = {width: w, height: h};
37524             return false;
37525         }
37526     },
37527     /**
37528      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37529      * @return {Roo.UpdateManager} The UpdateManager
37530      */
37531     getUpdateManager : function(){
37532         return this.el.getUpdateManager();
37533     },
37534      /**
37535      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37536      * @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:
37537 <pre><code>
37538 panel.load({
37539     url: "your-url.php",
37540     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37541     callback: yourFunction,
37542     scope: yourObject, //(optional scope)
37543     discardUrl: false,
37544     nocache: false,
37545     text: "Loading...",
37546     timeout: 30,
37547     scripts: false
37548 });
37549 </code></pre>
37550      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37551      * 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.
37552      * @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}
37553      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37554      * @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.
37555      * @return {Roo.ContentPanel} this
37556      */
37557     load : function(){
37558         var um = this.el.getUpdateManager();
37559         um.update.apply(um, arguments);
37560         return this;
37561     },
37562
37563
37564     /**
37565      * 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.
37566      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37567      * @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)
37568      * @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)
37569      * @return {Roo.UpdateManager} The UpdateManager
37570      */
37571     setUrl : function(url, params, loadOnce){
37572         if(this.refreshDelegate){
37573             this.removeListener("activate", this.refreshDelegate);
37574         }
37575         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37576         this.on("activate", this.refreshDelegate);
37577         return this.el.getUpdateManager();
37578     },
37579     
37580     _handleRefresh : function(url, params, loadOnce){
37581         if(!loadOnce || !this.loaded){
37582             var updater = this.el.getUpdateManager();
37583             updater.update(url, params, this._setLoaded.createDelegate(this));
37584         }
37585     },
37586     
37587     _setLoaded : function(){
37588         this.loaded = true;
37589     }, 
37590     
37591     /**
37592      * Returns this panel's id
37593      * @return {String} 
37594      */
37595     getId : function(){
37596         return this.el.id;
37597     },
37598     
37599     /** 
37600      * Returns this panel's element - used by regiosn to add.
37601      * @return {Roo.Element} 
37602      */
37603     getEl : function(){
37604         return this.wrapEl || this.el;
37605     },
37606     
37607    
37608     
37609     adjustForComponents : function(width, height)
37610     {
37611         //Roo.log('adjustForComponents ');
37612         if(this.resizeEl != this.el){
37613             width -= this.el.getFrameWidth('lr');
37614             height -= this.el.getFrameWidth('tb');
37615         }
37616         if(this.toolbar){
37617             var te = this.toolbar.getEl();
37618             te.setWidth(width);
37619             height -= te.getHeight();
37620         }
37621         if(this.footer){
37622             var te = this.footer.getEl();
37623             te.setWidth(width);
37624             height -= te.getHeight();
37625         }
37626         
37627         
37628         if(this.adjustments){
37629             width += this.adjustments[0];
37630             height += this.adjustments[1];
37631         }
37632         return {"width": width, "height": height};
37633     },
37634     
37635     setSize : function(width, height){
37636         if(this.fitToFrame && !this.ignoreResize(width, height)){
37637             if(this.fitContainer && this.resizeEl != this.el){
37638                 this.el.setSize(width, height);
37639             }
37640             var size = this.adjustForComponents(width, height);
37641             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37642             this.fireEvent('resize', this, size.width, size.height);
37643         }
37644     },
37645     
37646     /**
37647      * Returns this panel's title
37648      * @return {String} 
37649      */
37650     getTitle : function(){
37651         
37652         if (typeof(this.title) != 'object') {
37653             return this.title;
37654         }
37655         
37656         var t = '';
37657         for (var k in this.title) {
37658             if (!this.title.hasOwnProperty(k)) {
37659                 continue;
37660             }
37661             
37662             if (k.indexOf('-') >= 0) {
37663                 var s = k.split('-');
37664                 for (var i = 0; i<s.length; i++) {
37665                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37666                 }
37667             } else {
37668                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37669             }
37670         }
37671         return t;
37672     },
37673     
37674     /**
37675      * Set this panel's title
37676      * @param {String} title
37677      */
37678     setTitle : function(title){
37679         this.title = title;
37680         if(this.region){
37681             this.region.updatePanelTitle(this, title);
37682         }
37683     },
37684     
37685     /**
37686      * Returns true is this panel was configured to be closable
37687      * @return {Boolean} 
37688      */
37689     isClosable : function(){
37690         return this.closable;
37691     },
37692     
37693     beforeSlide : function(){
37694         this.el.clip();
37695         this.resizeEl.clip();
37696     },
37697     
37698     afterSlide : function(){
37699         this.el.unclip();
37700         this.resizeEl.unclip();
37701     },
37702     
37703     /**
37704      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37705      *   Will fail silently if the {@link #setUrl} method has not been called.
37706      *   This does not activate the panel, just updates its content.
37707      */
37708     refresh : function(){
37709         if(this.refreshDelegate){
37710            this.loaded = false;
37711            this.refreshDelegate();
37712         }
37713     },
37714     
37715     /**
37716      * Destroys this panel
37717      */
37718     destroy : function(){
37719         this.el.removeAllListeners();
37720         var tempEl = document.createElement("span");
37721         tempEl.appendChild(this.el.dom);
37722         tempEl.innerHTML = "";
37723         this.el.remove();
37724         this.el = null;
37725     },
37726     
37727     /**
37728      * form - if the content panel contains a form - this is a reference to it.
37729      * @type {Roo.form.Form}
37730      */
37731     form : false,
37732     /**
37733      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37734      *    This contains a reference to it.
37735      * @type {Roo.View}
37736      */
37737     view : false,
37738     
37739       /**
37740      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37741      * <pre><code>
37742
37743 layout.addxtype({
37744        xtype : 'Form',
37745        items: [ .... ]
37746    }
37747 );
37748
37749 </code></pre>
37750      * @param {Object} cfg Xtype definition of item to add.
37751      */
37752     
37753     
37754     getChildContainer: function () {
37755         return this.getEl();
37756     }
37757     
37758     
37759     /*
37760         var  ret = new Roo.factory(cfg);
37761         return ret;
37762         
37763         
37764         // add form..
37765         if (cfg.xtype.match(/^Form$/)) {
37766             
37767             var el;
37768             //if (this.footer) {
37769             //    el = this.footer.container.insertSibling(false, 'before');
37770             //} else {
37771                 el = this.el.createChild();
37772             //}
37773
37774             this.form = new  Roo.form.Form(cfg);
37775             
37776             
37777             if ( this.form.allItems.length) {
37778                 this.form.render(el.dom);
37779             }
37780             return this.form;
37781         }
37782         // should only have one of theses..
37783         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37784             // views.. should not be just added - used named prop 'view''
37785             
37786             cfg.el = this.el.appendChild(document.createElement("div"));
37787             // factory?
37788             
37789             var ret = new Roo.factory(cfg);
37790              
37791              ret.render && ret.render(false, ''); // render blank..
37792             this.view = ret;
37793             return ret;
37794         }
37795         return false;
37796     }
37797     \*/
37798 });
37799  
37800 /**
37801  * @class Roo.bootstrap.panel.Grid
37802  * @extends Roo.bootstrap.panel.Content
37803  * @constructor
37804  * Create a new GridPanel.
37805  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37806  * @param {Object} config A the config object
37807   
37808  */
37809
37810
37811
37812 Roo.bootstrap.panel.Grid = function(config)
37813 {
37814     
37815       
37816     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37817         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37818
37819     config.el = this.wrapper;
37820     //this.el = this.wrapper;
37821     
37822       if (config.container) {
37823         // ctor'ed from a Border/panel.grid
37824         
37825         
37826         this.wrapper.setStyle("overflow", "hidden");
37827         this.wrapper.addClass('roo-grid-container');
37828
37829     }
37830     
37831     
37832     if(config.toolbar){
37833         var tool_el = this.wrapper.createChild();    
37834         this.toolbar = Roo.factory(config.toolbar);
37835         var ti = [];
37836         if (config.toolbar.items) {
37837             ti = config.toolbar.items ;
37838             delete config.toolbar.items ;
37839         }
37840         
37841         var nitems = [];
37842         this.toolbar.render(tool_el);
37843         for(var i =0;i < ti.length;i++) {
37844           //  Roo.log(['add child', items[i]]);
37845             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37846         }
37847         this.toolbar.items = nitems;
37848         
37849         delete config.toolbar;
37850     }
37851     
37852     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37853     config.grid.scrollBody = true;;
37854     config.grid.monitorWindowResize = false; // turn off autosizing
37855     config.grid.autoHeight = false;
37856     config.grid.autoWidth = false;
37857     
37858     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37859     
37860     if (config.background) {
37861         // render grid on panel activation (if panel background)
37862         this.on('activate', function(gp) {
37863             if (!gp.grid.rendered) {
37864                 gp.grid.render(this.wrapper);
37865                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37866             }
37867         });
37868             
37869     } else {
37870         this.grid.render(this.wrapper);
37871         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37872
37873     }
37874     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37875     // ??? needed ??? config.el = this.wrapper;
37876     
37877     
37878     
37879   
37880     // xtype created footer. - not sure if will work as we normally have to render first..
37881     if (this.footer && !this.footer.el && this.footer.xtype) {
37882         
37883         var ctr = this.grid.getView().getFooterPanel(true);
37884         this.footer.dataSource = this.grid.dataSource;
37885         this.footer = Roo.factory(this.footer, Roo);
37886         this.footer.render(ctr);
37887         
37888     }
37889     
37890     
37891     
37892     
37893      
37894 };
37895
37896 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37897     getId : function(){
37898         return this.grid.id;
37899     },
37900     
37901     /**
37902      * Returns the grid for this panel
37903      * @return {Roo.bootstrap.Table} 
37904      */
37905     getGrid : function(){
37906         return this.grid;    
37907     },
37908     
37909     setSize : function(width, height){
37910         if(!this.ignoreResize(width, height)){
37911             var grid = this.grid;
37912             var size = this.adjustForComponents(width, height);
37913             var gridel = grid.getGridEl();
37914             gridel.setSize(size.width, size.height);
37915             /*
37916             var thd = grid.getGridEl().select('thead',true).first();
37917             var tbd = grid.getGridEl().select('tbody', true).first();
37918             if (tbd) {
37919                 tbd.setSize(width, height - thd.getHeight());
37920             }
37921             */
37922             grid.autoSize();
37923         }
37924     },
37925      
37926     
37927     
37928     beforeSlide : function(){
37929         this.grid.getView().scroller.clip();
37930     },
37931     
37932     afterSlide : function(){
37933         this.grid.getView().scroller.unclip();
37934     },
37935     
37936     destroy : function(){
37937         this.grid.destroy();
37938         delete this.grid;
37939         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37940     }
37941 });
37942
37943 /**
37944  * @class Roo.bootstrap.panel.Nest
37945  * @extends Roo.bootstrap.panel.Content
37946  * @constructor
37947  * Create a new Panel, that can contain a layout.Border.
37948  * 
37949  * 
37950  * @param {Roo.BorderLayout} layout The layout for this panel
37951  * @param {String/Object} config A string to set only the title or a config object
37952  */
37953 Roo.bootstrap.panel.Nest = function(config)
37954 {
37955     // construct with only one argument..
37956     /* FIXME - implement nicer consturctors
37957     if (layout.layout) {
37958         config = layout;
37959         layout = config.layout;
37960         delete config.layout;
37961     }
37962     if (layout.xtype && !layout.getEl) {
37963         // then layout needs constructing..
37964         layout = Roo.factory(layout, Roo);
37965     }
37966     */
37967     
37968     config.el =  config.layout.getEl();
37969     
37970     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37971     
37972     config.layout.monitorWindowResize = false; // turn off autosizing
37973     this.layout = config.layout;
37974     this.layout.getEl().addClass("roo-layout-nested-layout");
37975     this.layout.parent = this;
37976     
37977     
37978     
37979     
37980 };
37981
37982 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37983
37984     setSize : function(width, height){
37985         if(!this.ignoreResize(width, height)){
37986             var size = this.adjustForComponents(width, height);
37987             var el = this.layout.getEl();
37988             if (size.height < 1) {
37989                 el.setWidth(size.width);   
37990             } else {
37991                 el.setSize(size.width, size.height);
37992             }
37993             var touch = el.dom.offsetWidth;
37994             this.layout.layout();
37995             // ie requires a double layout on the first pass
37996             if(Roo.isIE && !this.initialized){
37997                 this.initialized = true;
37998                 this.layout.layout();
37999             }
38000         }
38001     },
38002     
38003     // activate all subpanels if not currently active..
38004     
38005     setActiveState : function(active){
38006         this.active = active;
38007         this.setActiveClass(active);
38008         
38009         if(!active){
38010             this.fireEvent("deactivate", this);
38011             return;
38012         }
38013         
38014         this.fireEvent("activate", this);
38015         // not sure if this should happen before or after..
38016         if (!this.layout) {
38017             return; // should not happen..
38018         }
38019         var reg = false;
38020         for (var r in this.layout.regions) {
38021             reg = this.layout.getRegion(r);
38022             if (reg.getActivePanel()) {
38023                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
38024                 reg.setActivePanel(reg.getActivePanel());
38025                 continue;
38026             }
38027             if (!reg.panels.length) {
38028                 continue;
38029             }
38030             reg.showPanel(reg.getPanel(0));
38031         }
38032         
38033         
38034         
38035         
38036     },
38037     
38038     /**
38039      * Returns the nested BorderLayout for this panel
38040      * @return {Roo.BorderLayout} 
38041      */
38042     getLayout : function(){
38043         return this.layout;
38044     },
38045     
38046      /**
38047      * Adds a xtype elements to the layout of the nested panel
38048      * <pre><code>
38049
38050 panel.addxtype({
38051        xtype : 'ContentPanel',
38052        region: 'west',
38053        items: [ .... ]
38054    }
38055 );
38056
38057 panel.addxtype({
38058         xtype : 'NestedLayoutPanel',
38059         region: 'west',
38060         layout: {
38061            center: { },
38062            west: { }   
38063         },
38064         items : [ ... list of content panels or nested layout panels.. ]
38065    }
38066 );
38067 </code></pre>
38068      * @param {Object} cfg Xtype definition of item to add.
38069      */
38070     addxtype : function(cfg) {
38071         return this.layout.addxtype(cfg);
38072     
38073     }
38074 });/*
38075  * Based on:
38076  * Ext JS Library 1.1.1
38077  * Copyright(c) 2006-2007, Ext JS, LLC.
38078  *
38079  * Originally Released Under LGPL - original licence link has changed is not relivant.
38080  *
38081  * Fork - LGPL
38082  * <script type="text/javascript">
38083  */
38084 /**
38085  * @class Roo.TabPanel
38086  * @extends Roo.util.Observable
38087  * A lightweight tab container.
38088  * <br><br>
38089  * Usage:
38090  * <pre><code>
38091 // basic tabs 1, built from existing content
38092 var tabs = new Roo.TabPanel("tabs1");
38093 tabs.addTab("script", "View Script");
38094 tabs.addTab("markup", "View Markup");
38095 tabs.activate("script");
38096
38097 // more advanced tabs, built from javascript
38098 var jtabs = new Roo.TabPanel("jtabs");
38099 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38100
38101 // set up the UpdateManager
38102 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38103 var updater = tab2.getUpdateManager();
38104 updater.setDefaultUrl("ajax1.htm");
38105 tab2.on('activate', updater.refresh, updater, true);
38106
38107 // Use setUrl for Ajax loading
38108 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38109 tab3.setUrl("ajax2.htm", null, true);
38110
38111 // Disabled tab
38112 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38113 tab4.disable();
38114
38115 jtabs.activate("jtabs-1");
38116  * </code></pre>
38117  * @constructor
38118  * Create a new TabPanel.
38119  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38120  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38121  */
38122 Roo.bootstrap.panel.Tabs = function(config){
38123     /**
38124     * The container element for this TabPanel.
38125     * @type Roo.Element
38126     */
38127     this.el = Roo.get(config.el);
38128     delete config.el;
38129     if(config){
38130         if(typeof config == "boolean"){
38131             this.tabPosition = config ? "bottom" : "top";
38132         }else{
38133             Roo.apply(this, config);
38134         }
38135     }
38136     
38137     if(this.tabPosition == "bottom"){
38138         // if tabs are at the bottom = create the body first.
38139         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38140         this.el.addClass("roo-tabs-bottom");
38141     }
38142     // next create the tabs holders
38143     
38144     if (this.tabPosition == "west"){
38145         
38146         var reg = this.region; // fake it..
38147         while (reg) {
38148             if (!reg.mgr.parent) {
38149                 break;
38150             }
38151             reg = reg.mgr.parent.region;
38152         }
38153         Roo.log("got nest?");
38154         Roo.log(reg);
38155         if (reg.mgr.getRegion('west')) {
38156             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38157             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38158             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38159             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38160             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38161         
38162             
38163         }
38164         
38165         
38166     } else {
38167      
38168         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38169         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38170         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38171         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38172     }
38173     
38174     
38175     if(Roo.isIE){
38176         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38177     }
38178     
38179     // finally - if tabs are at the top, then create the body last..
38180     if(this.tabPosition != "bottom"){
38181         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38182          * @type Roo.Element
38183          */
38184         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38185         this.el.addClass("roo-tabs-top");
38186     }
38187     this.items = [];
38188
38189     this.bodyEl.setStyle("position", "relative");
38190
38191     this.active = null;
38192     this.activateDelegate = this.activate.createDelegate(this);
38193
38194     this.addEvents({
38195         /**
38196          * @event tabchange
38197          * Fires when the active tab changes
38198          * @param {Roo.TabPanel} this
38199          * @param {Roo.TabPanelItem} activePanel The new active tab
38200          */
38201         "tabchange": true,
38202         /**
38203          * @event beforetabchange
38204          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38205          * @param {Roo.TabPanel} this
38206          * @param {Object} e Set cancel to true on this object to cancel the tab change
38207          * @param {Roo.TabPanelItem} tab The tab being changed to
38208          */
38209         "beforetabchange" : true
38210     });
38211
38212     Roo.EventManager.onWindowResize(this.onResize, this);
38213     this.cpad = this.el.getPadding("lr");
38214     this.hiddenCount = 0;
38215
38216
38217     // toolbar on the tabbar support...
38218     if (this.toolbar) {
38219         alert("no toolbar support yet");
38220         this.toolbar  = false;
38221         /*
38222         var tcfg = this.toolbar;
38223         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38224         this.toolbar = new Roo.Toolbar(tcfg);
38225         if (Roo.isSafari) {
38226             var tbl = tcfg.container.child('table', true);
38227             tbl.setAttribute('width', '100%');
38228         }
38229         */
38230         
38231     }
38232    
38233
38234
38235     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38236 };
38237
38238 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38239     /*
38240      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38241      */
38242     tabPosition : "top",
38243     /*
38244      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38245      */
38246     currentTabWidth : 0,
38247     /*
38248      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38249      */
38250     minTabWidth : 40,
38251     /*
38252      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38253      */
38254     maxTabWidth : 250,
38255     /*
38256      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38257      */
38258     preferredTabWidth : 175,
38259     /*
38260      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38261      */
38262     resizeTabs : false,
38263     /*
38264      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38265      */
38266     monitorResize : true,
38267     /*
38268      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38269      */
38270     toolbar : false,  // set by caller..
38271     
38272     region : false, /// set by caller
38273     
38274     disableTooltips : true, // not used yet...
38275
38276     /**
38277      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38278      * @param {String} id The id of the div to use <b>or create</b>
38279      * @param {String} text The text for the tab
38280      * @param {String} content (optional) Content to put in the TabPanelItem body
38281      * @param {Boolean} closable (optional) True to create a close icon on the tab
38282      * @return {Roo.TabPanelItem} The created TabPanelItem
38283      */
38284     addTab : function(id, text, content, closable, tpl)
38285     {
38286         var item = new Roo.bootstrap.panel.TabItem({
38287             panel: this,
38288             id : id,
38289             text : text,
38290             closable : closable,
38291             tpl : tpl
38292         });
38293         this.addTabItem(item);
38294         if(content){
38295             item.setContent(content);
38296         }
38297         return item;
38298     },
38299
38300     /**
38301      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38302      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38303      * @return {Roo.TabPanelItem}
38304      */
38305     getTab : function(id){
38306         return this.items[id];
38307     },
38308
38309     /**
38310      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38311      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38312      */
38313     hideTab : function(id){
38314         var t = this.items[id];
38315         if(!t.isHidden()){
38316            t.setHidden(true);
38317            this.hiddenCount++;
38318            this.autoSizeTabs();
38319         }
38320     },
38321
38322     /**
38323      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38324      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38325      */
38326     unhideTab : function(id){
38327         var t = this.items[id];
38328         if(t.isHidden()){
38329            t.setHidden(false);
38330            this.hiddenCount--;
38331            this.autoSizeTabs();
38332         }
38333     },
38334
38335     /**
38336      * Adds an existing {@link Roo.TabPanelItem}.
38337      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38338      */
38339     addTabItem : function(item)
38340     {
38341         this.items[item.id] = item;
38342         this.items.push(item);
38343         this.autoSizeTabs();
38344       //  if(this.resizeTabs){
38345     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38346   //         this.autoSizeTabs();
38347 //        }else{
38348 //            item.autoSize();
38349        // }
38350     },
38351
38352     /**
38353      * Removes a {@link Roo.TabPanelItem}.
38354      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38355      */
38356     removeTab : function(id){
38357         var items = this.items;
38358         var tab = items[id];
38359         if(!tab) { return; }
38360         var index = items.indexOf(tab);
38361         if(this.active == tab && items.length > 1){
38362             var newTab = this.getNextAvailable(index);
38363             if(newTab) {
38364                 newTab.activate();
38365             }
38366         }
38367         this.stripEl.dom.removeChild(tab.pnode.dom);
38368         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38369             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38370         }
38371         items.splice(index, 1);
38372         delete this.items[tab.id];
38373         tab.fireEvent("close", tab);
38374         tab.purgeListeners();
38375         this.autoSizeTabs();
38376     },
38377
38378     getNextAvailable : function(start){
38379         var items = this.items;
38380         var index = start;
38381         // look for a next tab that will slide over to
38382         // replace the one being removed
38383         while(index < items.length){
38384             var item = items[++index];
38385             if(item && !item.isHidden()){
38386                 return item;
38387             }
38388         }
38389         // if one isn't found select the previous tab (on the left)
38390         index = start;
38391         while(index >= 0){
38392             var item = items[--index];
38393             if(item && !item.isHidden()){
38394                 return item;
38395             }
38396         }
38397         return null;
38398     },
38399
38400     /**
38401      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38402      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38403      */
38404     disableTab : function(id){
38405         var tab = this.items[id];
38406         if(tab && this.active != tab){
38407             tab.disable();
38408         }
38409     },
38410
38411     /**
38412      * Enables a {@link Roo.TabPanelItem} that is disabled.
38413      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38414      */
38415     enableTab : function(id){
38416         var tab = this.items[id];
38417         tab.enable();
38418     },
38419
38420     /**
38421      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38422      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38423      * @return {Roo.TabPanelItem} The TabPanelItem.
38424      */
38425     activate : function(id)
38426     {
38427         //Roo.log('activite:'  + id);
38428         
38429         var tab = this.items[id];
38430         if(!tab){
38431             return null;
38432         }
38433         if(tab == this.active || tab.disabled){
38434             return tab;
38435         }
38436         var e = {};
38437         this.fireEvent("beforetabchange", this, e, tab);
38438         if(e.cancel !== true && !tab.disabled){
38439             if(this.active){
38440                 this.active.hide();
38441             }
38442             this.active = this.items[id];
38443             this.active.show();
38444             this.fireEvent("tabchange", this, this.active);
38445         }
38446         return tab;
38447     },
38448
38449     /**
38450      * Gets the active {@link Roo.TabPanelItem}.
38451      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38452      */
38453     getActiveTab : function(){
38454         return this.active;
38455     },
38456
38457     /**
38458      * Updates the tab body element to fit the height of the container element
38459      * for overflow scrolling
38460      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38461      */
38462     syncHeight : function(targetHeight){
38463         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38464         var bm = this.bodyEl.getMargins();
38465         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38466         this.bodyEl.setHeight(newHeight);
38467         return newHeight;
38468     },
38469
38470     onResize : function(){
38471         if(this.monitorResize){
38472             this.autoSizeTabs();
38473         }
38474     },
38475
38476     /**
38477      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38478      */
38479     beginUpdate : function(){
38480         this.updating = true;
38481     },
38482
38483     /**
38484      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38485      */
38486     endUpdate : function(){
38487         this.updating = false;
38488         this.autoSizeTabs();
38489     },
38490
38491     /**
38492      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38493      */
38494     autoSizeTabs : function()
38495     {
38496         var count = this.items.length;
38497         var vcount = count - this.hiddenCount;
38498         
38499         if (vcount < 2) {
38500             this.stripEl.hide();
38501         } else {
38502             this.stripEl.show();
38503         }
38504         
38505         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38506             return;
38507         }
38508         
38509         
38510         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38511         var availWidth = Math.floor(w / vcount);
38512         var b = this.stripBody;
38513         if(b.getWidth() > w){
38514             var tabs = this.items;
38515             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38516             if(availWidth < this.minTabWidth){
38517                 /*if(!this.sleft){    // incomplete scrolling code
38518                     this.createScrollButtons();
38519                 }
38520                 this.showScroll();
38521                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38522             }
38523         }else{
38524             if(this.currentTabWidth < this.preferredTabWidth){
38525                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38526             }
38527         }
38528     },
38529
38530     /**
38531      * Returns the number of tabs in this TabPanel.
38532      * @return {Number}
38533      */
38534      getCount : function(){
38535          return this.items.length;
38536      },
38537
38538     /**
38539      * Resizes all the tabs to the passed width
38540      * @param {Number} The new width
38541      */
38542     setTabWidth : function(width){
38543         this.currentTabWidth = width;
38544         for(var i = 0, len = this.items.length; i < len; i++) {
38545                 if(!this.items[i].isHidden()) {
38546                 this.items[i].setWidth(width);
38547             }
38548         }
38549     },
38550
38551     /**
38552      * Destroys this TabPanel
38553      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38554      */
38555     destroy : function(removeEl){
38556         Roo.EventManager.removeResizeListener(this.onResize, this);
38557         for(var i = 0, len = this.items.length; i < len; i++){
38558             this.items[i].purgeListeners();
38559         }
38560         if(removeEl === true){
38561             this.el.update("");
38562             this.el.remove();
38563         }
38564     },
38565     
38566     createStrip : function(container)
38567     {
38568         var strip = document.createElement("nav");
38569         strip.className = Roo.bootstrap.version == 4 ?
38570             "navbar-light bg-light" : 
38571             "navbar navbar-default"; //"x-tabs-wrap";
38572         container.appendChild(strip);
38573         return strip;
38574     },
38575     
38576     createStripList : function(strip)
38577     {
38578         // div wrapper for retard IE
38579         // returns the "tr" element.
38580         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38581         //'<div class="x-tabs-strip-wrap">'+
38582           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38583           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38584         return strip.firstChild; //.firstChild.firstChild.firstChild;
38585     },
38586     createBody : function(container)
38587     {
38588         var body = document.createElement("div");
38589         Roo.id(body, "tab-body");
38590         //Roo.fly(body).addClass("x-tabs-body");
38591         Roo.fly(body).addClass("tab-content");
38592         container.appendChild(body);
38593         return body;
38594     },
38595     createItemBody :function(bodyEl, id){
38596         var body = Roo.getDom(id);
38597         if(!body){
38598             body = document.createElement("div");
38599             body.id = id;
38600         }
38601         //Roo.fly(body).addClass("x-tabs-item-body");
38602         Roo.fly(body).addClass("tab-pane");
38603          bodyEl.insertBefore(body, bodyEl.firstChild);
38604         return body;
38605     },
38606     /** @private */
38607     createStripElements :  function(stripEl, text, closable, tpl)
38608     {
38609         var td = document.createElement("li"); // was td..
38610         td.className = 'nav-item';
38611         
38612         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38613         
38614         
38615         stripEl.appendChild(td);
38616         /*if(closable){
38617             td.className = "x-tabs-closable";
38618             if(!this.closeTpl){
38619                 this.closeTpl = new Roo.Template(
38620                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38621                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38622                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38623                 );
38624             }
38625             var el = this.closeTpl.overwrite(td, {"text": text});
38626             var close = el.getElementsByTagName("div")[0];
38627             var inner = el.getElementsByTagName("em")[0];
38628             return {"el": el, "close": close, "inner": inner};
38629         } else {
38630         */
38631         // not sure what this is..
38632 //            if(!this.tabTpl){
38633                 //this.tabTpl = new Roo.Template(
38634                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38635                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38636                 //);
38637 //                this.tabTpl = new Roo.Template(
38638 //                   '<a href="#">' +
38639 //                   '<span unselectable="on"' +
38640 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38641 //                            ' >{text}</span></a>'
38642 //                );
38643 //                
38644 //            }
38645
38646
38647             var template = tpl || this.tabTpl || false;
38648             
38649             if(!template){
38650                 template =  new Roo.Template(
38651                         Roo.bootstrap.version == 4 ? 
38652                             (
38653                                 '<a class="nav-link" href="#" unselectable="on"' +
38654                                      (this.disableTooltips ? '' : ' title="{text}"') +
38655                                      ' >{text}</a>'
38656                             ) : (
38657                                 '<a class="nav-link" href="#">' +
38658                                 '<span unselectable="on"' +
38659                                          (this.disableTooltips ? '' : ' title="{text}"') +
38660                                     ' >{text}</span></a>'
38661                             )
38662                 );
38663             }
38664             
38665             switch (typeof(template)) {
38666                 case 'object' :
38667                     break;
38668                 case 'string' :
38669                     template = new Roo.Template(template);
38670                     break;
38671                 default :
38672                     break;
38673             }
38674             
38675             var el = template.overwrite(td, {"text": text});
38676             
38677             var inner = el.getElementsByTagName("span")[0];
38678             
38679             return {"el": el, "inner": inner};
38680             
38681     }
38682         
38683     
38684 });
38685
38686 /**
38687  * @class Roo.TabPanelItem
38688  * @extends Roo.util.Observable
38689  * Represents an individual item (tab plus body) in a TabPanel.
38690  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38691  * @param {String} id The id of this TabPanelItem
38692  * @param {String} text The text for the tab of this TabPanelItem
38693  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38694  */
38695 Roo.bootstrap.panel.TabItem = function(config){
38696     /**
38697      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38698      * @type Roo.TabPanel
38699      */
38700     this.tabPanel = config.panel;
38701     /**
38702      * The id for this TabPanelItem
38703      * @type String
38704      */
38705     this.id = config.id;
38706     /** @private */
38707     this.disabled = false;
38708     /** @private */
38709     this.text = config.text;
38710     /** @private */
38711     this.loaded = false;
38712     this.closable = config.closable;
38713
38714     /**
38715      * The body element for this TabPanelItem.
38716      * @type Roo.Element
38717      */
38718     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38719     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38720     this.bodyEl.setStyle("display", "block");
38721     this.bodyEl.setStyle("zoom", "1");
38722     //this.hideAction();
38723
38724     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38725     /** @private */
38726     this.el = Roo.get(els.el);
38727     this.inner = Roo.get(els.inner, true);
38728      this.textEl = Roo.bootstrap.version == 4 ?
38729         this.el : Roo.get(this.el.dom.firstChild, true);
38730
38731     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38732     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38733
38734     
38735 //    this.el.on("mousedown", this.onTabMouseDown, this);
38736     this.el.on("click", this.onTabClick, this);
38737     /** @private */
38738     if(config.closable){
38739         var c = Roo.get(els.close, true);
38740         c.dom.title = this.closeText;
38741         c.addClassOnOver("close-over");
38742         c.on("click", this.closeClick, this);
38743      }
38744
38745     this.addEvents({
38746          /**
38747          * @event activate
38748          * Fires when this tab becomes the active tab.
38749          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38750          * @param {Roo.TabPanelItem} this
38751          */
38752         "activate": true,
38753         /**
38754          * @event beforeclose
38755          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38756          * @param {Roo.TabPanelItem} this
38757          * @param {Object} e Set cancel to true on this object to cancel the close.
38758          */
38759         "beforeclose": true,
38760         /**
38761          * @event close
38762          * Fires when this tab is closed.
38763          * @param {Roo.TabPanelItem} this
38764          */
38765          "close": true,
38766         /**
38767          * @event deactivate
38768          * Fires when this tab is no longer the active tab.
38769          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38770          * @param {Roo.TabPanelItem} this
38771          */
38772          "deactivate" : true
38773     });
38774     this.hidden = false;
38775
38776     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38777 };
38778
38779 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38780            {
38781     purgeListeners : function(){
38782        Roo.util.Observable.prototype.purgeListeners.call(this);
38783        this.el.removeAllListeners();
38784     },
38785     /**
38786      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38787      */
38788     show : function(){
38789         this.status_node.addClass("active");
38790         this.showAction();
38791         if(Roo.isOpera){
38792             this.tabPanel.stripWrap.repaint();
38793         }
38794         this.fireEvent("activate", this.tabPanel, this);
38795     },
38796
38797     /**
38798      * Returns true if this tab is the active tab.
38799      * @return {Boolean}
38800      */
38801     isActive : function(){
38802         return this.tabPanel.getActiveTab() == this;
38803     },
38804
38805     /**
38806      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38807      */
38808     hide : function(){
38809         this.status_node.removeClass("active");
38810         this.hideAction();
38811         this.fireEvent("deactivate", this.tabPanel, this);
38812     },
38813
38814     hideAction : function(){
38815         this.bodyEl.hide();
38816         this.bodyEl.setStyle("position", "absolute");
38817         this.bodyEl.setLeft("-20000px");
38818         this.bodyEl.setTop("-20000px");
38819     },
38820
38821     showAction : function(){
38822         this.bodyEl.setStyle("position", "relative");
38823         this.bodyEl.setTop("");
38824         this.bodyEl.setLeft("");
38825         this.bodyEl.show();
38826     },
38827
38828     /**
38829      * Set the tooltip for the tab.
38830      * @param {String} tooltip The tab's tooltip
38831      */
38832     setTooltip : function(text){
38833         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38834             this.textEl.dom.qtip = text;
38835             this.textEl.dom.removeAttribute('title');
38836         }else{
38837             this.textEl.dom.title = text;
38838         }
38839     },
38840
38841     onTabClick : function(e){
38842         e.preventDefault();
38843         this.tabPanel.activate(this.id);
38844     },
38845
38846     onTabMouseDown : function(e){
38847         e.preventDefault();
38848         this.tabPanel.activate(this.id);
38849     },
38850 /*
38851     getWidth : function(){
38852         return this.inner.getWidth();
38853     },
38854
38855     setWidth : function(width){
38856         var iwidth = width - this.linode.getPadding("lr");
38857         this.inner.setWidth(iwidth);
38858         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38859         this.linode.setWidth(width);
38860     },
38861 */
38862     /**
38863      * Show or hide the tab
38864      * @param {Boolean} hidden True to hide or false to show.
38865      */
38866     setHidden : function(hidden){
38867         this.hidden = hidden;
38868         this.linode.setStyle("display", hidden ? "none" : "");
38869     },
38870
38871     /**
38872      * Returns true if this tab is "hidden"
38873      * @return {Boolean}
38874      */
38875     isHidden : function(){
38876         return this.hidden;
38877     },
38878
38879     /**
38880      * Returns the text for this tab
38881      * @return {String}
38882      */
38883     getText : function(){
38884         return this.text;
38885     },
38886     /*
38887     autoSize : function(){
38888         //this.el.beginMeasure();
38889         this.textEl.setWidth(1);
38890         /*
38891          *  #2804 [new] Tabs in Roojs
38892          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38893          */
38894         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38895         //this.el.endMeasure();
38896     //},
38897
38898     /**
38899      * Sets the text for the tab (Note: this also sets the tooltip text)
38900      * @param {String} text The tab's text and tooltip
38901      */
38902     setText : function(text){
38903         this.text = text;
38904         this.textEl.update(text);
38905         this.setTooltip(text);
38906         //if(!this.tabPanel.resizeTabs){
38907         //    this.autoSize();
38908         //}
38909     },
38910     /**
38911      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38912      */
38913     activate : function(){
38914         this.tabPanel.activate(this.id);
38915     },
38916
38917     /**
38918      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38919      */
38920     disable : function(){
38921         if(this.tabPanel.active != this){
38922             this.disabled = true;
38923             this.status_node.addClass("disabled");
38924         }
38925     },
38926
38927     /**
38928      * Enables this TabPanelItem if it was previously disabled.
38929      */
38930     enable : function(){
38931         this.disabled = false;
38932         this.status_node.removeClass("disabled");
38933     },
38934
38935     /**
38936      * Sets the content for this TabPanelItem.
38937      * @param {String} content The content
38938      * @param {Boolean} loadScripts true to look for and load scripts
38939      */
38940     setContent : function(content, loadScripts){
38941         this.bodyEl.update(content, loadScripts);
38942     },
38943
38944     /**
38945      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38946      * @return {Roo.UpdateManager} The UpdateManager
38947      */
38948     getUpdateManager : function(){
38949         return this.bodyEl.getUpdateManager();
38950     },
38951
38952     /**
38953      * Set a URL to be used to load the content for this TabPanelItem.
38954      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38955      * @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)
38956      * @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)
38957      * @return {Roo.UpdateManager} The UpdateManager
38958      */
38959     setUrl : function(url, params, loadOnce){
38960         if(this.refreshDelegate){
38961             this.un('activate', this.refreshDelegate);
38962         }
38963         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38964         this.on("activate", this.refreshDelegate);
38965         return this.bodyEl.getUpdateManager();
38966     },
38967
38968     /** @private */
38969     _handleRefresh : function(url, params, loadOnce){
38970         if(!loadOnce || !this.loaded){
38971             var updater = this.bodyEl.getUpdateManager();
38972             updater.update(url, params, this._setLoaded.createDelegate(this));
38973         }
38974     },
38975
38976     /**
38977      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38978      *   Will fail silently if the setUrl method has not been called.
38979      *   This does not activate the panel, just updates its content.
38980      */
38981     refresh : function(){
38982         if(this.refreshDelegate){
38983            this.loaded = false;
38984            this.refreshDelegate();
38985         }
38986     },
38987
38988     /** @private */
38989     _setLoaded : function(){
38990         this.loaded = true;
38991     },
38992
38993     /** @private */
38994     closeClick : function(e){
38995         var o = {};
38996         e.stopEvent();
38997         this.fireEvent("beforeclose", this, o);
38998         if(o.cancel !== true){
38999             this.tabPanel.removeTab(this.id);
39000         }
39001     },
39002     /**
39003      * The text displayed in the tooltip for the close icon.
39004      * @type String
39005      */
39006     closeText : "Close this tab"
39007 });
39008 /**
39009 *    This script refer to:
39010 *    Title: International Telephone Input
39011 *    Author: Jack O'Connor
39012 *    Code version:  v12.1.12
39013 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39014 **/
39015
39016 Roo.bootstrap.PhoneInputData = function() {
39017     var d = [
39018       [
39019         "Afghanistan (‫افغانستان‬‎)",
39020         "af",
39021         "93"
39022       ],
39023       [
39024         "Albania (Shqipëri)",
39025         "al",
39026         "355"
39027       ],
39028       [
39029         "Algeria (‫الجزائر‬‎)",
39030         "dz",
39031         "213"
39032       ],
39033       [
39034         "American Samoa",
39035         "as",
39036         "1684"
39037       ],
39038       [
39039         "Andorra",
39040         "ad",
39041         "376"
39042       ],
39043       [
39044         "Angola",
39045         "ao",
39046         "244"
39047       ],
39048       [
39049         "Anguilla",
39050         "ai",
39051         "1264"
39052       ],
39053       [
39054         "Antigua and Barbuda",
39055         "ag",
39056         "1268"
39057       ],
39058       [
39059         "Argentina",
39060         "ar",
39061         "54"
39062       ],
39063       [
39064         "Armenia (Հայաստան)",
39065         "am",
39066         "374"
39067       ],
39068       [
39069         "Aruba",
39070         "aw",
39071         "297"
39072       ],
39073       [
39074         "Australia",
39075         "au",
39076         "61",
39077         0
39078       ],
39079       [
39080         "Austria (Österreich)",
39081         "at",
39082         "43"
39083       ],
39084       [
39085         "Azerbaijan (Azərbaycan)",
39086         "az",
39087         "994"
39088       ],
39089       [
39090         "Bahamas",
39091         "bs",
39092         "1242"
39093       ],
39094       [
39095         "Bahrain (‫البحرين‬‎)",
39096         "bh",
39097         "973"
39098       ],
39099       [
39100         "Bangladesh (বাংলাদেশ)",
39101         "bd",
39102         "880"
39103       ],
39104       [
39105         "Barbados",
39106         "bb",
39107         "1246"
39108       ],
39109       [
39110         "Belarus (Беларусь)",
39111         "by",
39112         "375"
39113       ],
39114       [
39115         "Belgium (België)",
39116         "be",
39117         "32"
39118       ],
39119       [
39120         "Belize",
39121         "bz",
39122         "501"
39123       ],
39124       [
39125         "Benin (Bénin)",
39126         "bj",
39127         "229"
39128       ],
39129       [
39130         "Bermuda",
39131         "bm",
39132         "1441"
39133       ],
39134       [
39135         "Bhutan (འབྲུག)",
39136         "bt",
39137         "975"
39138       ],
39139       [
39140         "Bolivia",
39141         "bo",
39142         "591"
39143       ],
39144       [
39145         "Bosnia and Herzegovina (Босна и Херцеговина)",
39146         "ba",
39147         "387"
39148       ],
39149       [
39150         "Botswana",
39151         "bw",
39152         "267"
39153       ],
39154       [
39155         "Brazil (Brasil)",
39156         "br",
39157         "55"
39158       ],
39159       [
39160         "British Indian Ocean Territory",
39161         "io",
39162         "246"
39163       ],
39164       [
39165         "British Virgin Islands",
39166         "vg",
39167         "1284"
39168       ],
39169       [
39170         "Brunei",
39171         "bn",
39172         "673"
39173       ],
39174       [
39175         "Bulgaria (България)",
39176         "bg",
39177         "359"
39178       ],
39179       [
39180         "Burkina Faso",
39181         "bf",
39182         "226"
39183       ],
39184       [
39185         "Burundi (Uburundi)",
39186         "bi",
39187         "257"
39188       ],
39189       [
39190         "Cambodia (កម្ពុជា)",
39191         "kh",
39192         "855"
39193       ],
39194       [
39195         "Cameroon (Cameroun)",
39196         "cm",
39197         "237"
39198       ],
39199       [
39200         "Canada",
39201         "ca",
39202         "1",
39203         1,
39204         ["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"]
39205       ],
39206       [
39207         "Cape Verde (Kabu Verdi)",
39208         "cv",
39209         "238"
39210       ],
39211       [
39212         "Caribbean Netherlands",
39213         "bq",
39214         "599",
39215         1
39216       ],
39217       [
39218         "Cayman Islands",
39219         "ky",
39220         "1345"
39221       ],
39222       [
39223         "Central African Republic (République centrafricaine)",
39224         "cf",
39225         "236"
39226       ],
39227       [
39228         "Chad (Tchad)",
39229         "td",
39230         "235"
39231       ],
39232       [
39233         "Chile",
39234         "cl",
39235         "56"
39236       ],
39237       [
39238         "China (中国)",
39239         "cn",
39240         "86"
39241       ],
39242       [
39243         "Christmas Island",
39244         "cx",
39245         "61",
39246         2
39247       ],
39248       [
39249         "Cocos (Keeling) Islands",
39250         "cc",
39251         "61",
39252         1
39253       ],
39254       [
39255         "Colombia",
39256         "co",
39257         "57"
39258       ],
39259       [
39260         "Comoros (‫جزر القمر‬‎)",
39261         "km",
39262         "269"
39263       ],
39264       [
39265         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39266         "cd",
39267         "243"
39268       ],
39269       [
39270         "Congo (Republic) (Congo-Brazzaville)",
39271         "cg",
39272         "242"
39273       ],
39274       [
39275         "Cook Islands",
39276         "ck",
39277         "682"
39278       ],
39279       [
39280         "Costa Rica",
39281         "cr",
39282         "506"
39283       ],
39284       [
39285         "Côte d’Ivoire",
39286         "ci",
39287         "225"
39288       ],
39289       [
39290         "Croatia (Hrvatska)",
39291         "hr",
39292         "385"
39293       ],
39294       [
39295         "Cuba",
39296         "cu",
39297         "53"
39298       ],
39299       [
39300         "Curaçao",
39301         "cw",
39302         "599",
39303         0
39304       ],
39305       [
39306         "Cyprus (Κύπρος)",
39307         "cy",
39308         "357"
39309       ],
39310       [
39311         "Czech Republic (Česká republika)",
39312         "cz",
39313         "420"
39314       ],
39315       [
39316         "Denmark (Danmark)",
39317         "dk",
39318         "45"
39319       ],
39320       [
39321         "Djibouti",
39322         "dj",
39323         "253"
39324       ],
39325       [
39326         "Dominica",
39327         "dm",
39328         "1767"
39329       ],
39330       [
39331         "Dominican Republic (República Dominicana)",
39332         "do",
39333         "1",
39334         2,
39335         ["809", "829", "849"]
39336       ],
39337       [
39338         "Ecuador",
39339         "ec",
39340         "593"
39341       ],
39342       [
39343         "Egypt (‫مصر‬‎)",
39344         "eg",
39345         "20"
39346       ],
39347       [
39348         "El Salvador",
39349         "sv",
39350         "503"
39351       ],
39352       [
39353         "Equatorial Guinea (Guinea Ecuatorial)",
39354         "gq",
39355         "240"
39356       ],
39357       [
39358         "Eritrea",
39359         "er",
39360         "291"
39361       ],
39362       [
39363         "Estonia (Eesti)",
39364         "ee",
39365         "372"
39366       ],
39367       [
39368         "Ethiopia",
39369         "et",
39370         "251"
39371       ],
39372       [
39373         "Falkland Islands (Islas Malvinas)",
39374         "fk",
39375         "500"
39376       ],
39377       [
39378         "Faroe Islands (Føroyar)",
39379         "fo",
39380         "298"
39381       ],
39382       [
39383         "Fiji",
39384         "fj",
39385         "679"
39386       ],
39387       [
39388         "Finland (Suomi)",
39389         "fi",
39390         "358",
39391         0
39392       ],
39393       [
39394         "France",
39395         "fr",
39396         "33"
39397       ],
39398       [
39399         "French Guiana (Guyane française)",
39400         "gf",
39401         "594"
39402       ],
39403       [
39404         "French Polynesia (Polynésie française)",
39405         "pf",
39406         "689"
39407       ],
39408       [
39409         "Gabon",
39410         "ga",
39411         "241"
39412       ],
39413       [
39414         "Gambia",
39415         "gm",
39416         "220"
39417       ],
39418       [
39419         "Georgia (საქართველო)",
39420         "ge",
39421         "995"
39422       ],
39423       [
39424         "Germany (Deutschland)",
39425         "de",
39426         "49"
39427       ],
39428       [
39429         "Ghana (Gaana)",
39430         "gh",
39431         "233"
39432       ],
39433       [
39434         "Gibraltar",
39435         "gi",
39436         "350"
39437       ],
39438       [
39439         "Greece (Ελλάδα)",
39440         "gr",
39441         "30"
39442       ],
39443       [
39444         "Greenland (Kalaallit Nunaat)",
39445         "gl",
39446         "299"
39447       ],
39448       [
39449         "Grenada",
39450         "gd",
39451         "1473"
39452       ],
39453       [
39454         "Guadeloupe",
39455         "gp",
39456         "590",
39457         0
39458       ],
39459       [
39460         "Guam",
39461         "gu",
39462         "1671"
39463       ],
39464       [
39465         "Guatemala",
39466         "gt",
39467         "502"
39468       ],
39469       [
39470         "Guernsey",
39471         "gg",
39472         "44",
39473         1
39474       ],
39475       [
39476         "Guinea (Guinée)",
39477         "gn",
39478         "224"
39479       ],
39480       [
39481         "Guinea-Bissau (Guiné Bissau)",
39482         "gw",
39483         "245"
39484       ],
39485       [
39486         "Guyana",
39487         "gy",
39488         "592"
39489       ],
39490       [
39491         "Haiti",
39492         "ht",
39493         "509"
39494       ],
39495       [
39496         "Honduras",
39497         "hn",
39498         "504"
39499       ],
39500       [
39501         "Hong Kong (香港)",
39502         "hk",
39503         "852"
39504       ],
39505       [
39506         "Hungary (Magyarország)",
39507         "hu",
39508         "36"
39509       ],
39510       [
39511         "Iceland (Ísland)",
39512         "is",
39513         "354"
39514       ],
39515       [
39516         "India (भारत)",
39517         "in",
39518         "91"
39519       ],
39520       [
39521         "Indonesia",
39522         "id",
39523         "62"
39524       ],
39525       [
39526         "Iran (‫ایران‬‎)",
39527         "ir",
39528         "98"
39529       ],
39530       [
39531         "Iraq (‫العراق‬‎)",
39532         "iq",
39533         "964"
39534       ],
39535       [
39536         "Ireland",
39537         "ie",
39538         "353"
39539       ],
39540       [
39541         "Isle of Man",
39542         "im",
39543         "44",
39544         2
39545       ],
39546       [
39547         "Israel (‫ישראל‬‎)",
39548         "il",
39549         "972"
39550       ],
39551       [
39552         "Italy (Italia)",
39553         "it",
39554         "39",
39555         0
39556       ],
39557       [
39558         "Jamaica",
39559         "jm",
39560         "1876"
39561       ],
39562       [
39563         "Japan (日本)",
39564         "jp",
39565         "81"
39566       ],
39567       [
39568         "Jersey",
39569         "je",
39570         "44",
39571         3
39572       ],
39573       [
39574         "Jordan (‫الأردن‬‎)",
39575         "jo",
39576         "962"
39577       ],
39578       [
39579         "Kazakhstan (Казахстан)",
39580         "kz",
39581         "7",
39582         1
39583       ],
39584       [
39585         "Kenya",
39586         "ke",
39587         "254"
39588       ],
39589       [
39590         "Kiribati",
39591         "ki",
39592         "686"
39593       ],
39594       [
39595         "Kosovo",
39596         "xk",
39597         "383"
39598       ],
39599       [
39600         "Kuwait (‫الكويت‬‎)",
39601         "kw",
39602         "965"
39603       ],
39604       [
39605         "Kyrgyzstan (Кыргызстан)",
39606         "kg",
39607         "996"
39608       ],
39609       [
39610         "Laos (ລາວ)",
39611         "la",
39612         "856"
39613       ],
39614       [
39615         "Latvia (Latvija)",
39616         "lv",
39617         "371"
39618       ],
39619       [
39620         "Lebanon (‫لبنان‬‎)",
39621         "lb",
39622         "961"
39623       ],
39624       [
39625         "Lesotho",
39626         "ls",
39627         "266"
39628       ],
39629       [
39630         "Liberia",
39631         "lr",
39632         "231"
39633       ],
39634       [
39635         "Libya (‫ليبيا‬‎)",
39636         "ly",
39637         "218"
39638       ],
39639       [
39640         "Liechtenstein",
39641         "li",
39642         "423"
39643       ],
39644       [
39645         "Lithuania (Lietuva)",
39646         "lt",
39647         "370"
39648       ],
39649       [
39650         "Luxembourg",
39651         "lu",
39652         "352"
39653       ],
39654       [
39655         "Macau (澳門)",
39656         "mo",
39657         "853"
39658       ],
39659       [
39660         "Macedonia (FYROM) (Македонија)",
39661         "mk",
39662         "389"
39663       ],
39664       [
39665         "Madagascar (Madagasikara)",
39666         "mg",
39667         "261"
39668       ],
39669       [
39670         "Malawi",
39671         "mw",
39672         "265"
39673       ],
39674       [
39675         "Malaysia",
39676         "my",
39677         "60"
39678       ],
39679       [
39680         "Maldives",
39681         "mv",
39682         "960"
39683       ],
39684       [
39685         "Mali",
39686         "ml",
39687         "223"
39688       ],
39689       [
39690         "Malta",
39691         "mt",
39692         "356"
39693       ],
39694       [
39695         "Marshall Islands",
39696         "mh",
39697         "692"
39698       ],
39699       [
39700         "Martinique",
39701         "mq",
39702         "596"
39703       ],
39704       [
39705         "Mauritania (‫موريتانيا‬‎)",
39706         "mr",
39707         "222"
39708       ],
39709       [
39710         "Mauritius (Moris)",
39711         "mu",
39712         "230"
39713       ],
39714       [
39715         "Mayotte",
39716         "yt",
39717         "262",
39718         1
39719       ],
39720       [
39721         "Mexico (México)",
39722         "mx",
39723         "52"
39724       ],
39725       [
39726         "Micronesia",
39727         "fm",
39728         "691"
39729       ],
39730       [
39731         "Moldova (Republica Moldova)",
39732         "md",
39733         "373"
39734       ],
39735       [
39736         "Monaco",
39737         "mc",
39738         "377"
39739       ],
39740       [
39741         "Mongolia (Монгол)",
39742         "mn",
39743         "976"
39744       ],
39745       [
39746         "Montenegro (Crna Gora)",
39747         "me",
39748         "382"
39749       ],
39750       [
39751         "Montserrat",
39752         "ms",
39753         "1664"
39754       ],
39755       [
39756         "Morocco (‫المغرب‬‎)",
39757         "ma",
39758         "212",
39759         0
39760       ],
39761       [
39762         "Mozambique (Moçambique)",
39763         "mz",
39764         "258"
39765       ],
39766       [
39767         "Myanmar (Burma) (မြန်မာ)",
39768         "mm",
39769         "95"
39770       ],
39771       [
39772         "Namibia (Namibië)",
39773         "na",
39774         "264"
39775       ],
39776       [
39777         "Nauru",
39778         "nr",
39779         "674"
39780       ],
39781       [
39782         "Nepal (नेपाल)",
39783         "np",
39784         "977"
39785       ],
39786       [
39787         "Netherlands (Nederland)",
39788         "nl",
39789         "31"
39790       ],
39791       [
39792         "New Caledonia (Nouvelle-Calédonie)",
39793         "nc",
39794         "687"
39795       ],
39796       [
39797         "New Zealand",
39798         "nz",
39799         "64"
39800       ],
39801       [
39802         "Nicaragua",
39803         "ni",
39804         "505"
39805       ],
39806       [
39807         "Niger (Nijar)",
39808         "ne",
39809         "227"
39810       ],
39811       [
39812         "Nigeria",
39813         "ng",
39814         "234"
39815       ],
39816       [
39817         "Niue",
39818         "nu",
39819         "683"
39820       ],
39821       [
39822         "Norfolk Island",
39823         "nf",
39824         "672"
39825       ],
39826       [
39827         "North Korea (조선 민주주의 인민 공화국)",
39828         "kp",
39829         "850"
39830       ],
39831       [
39832         "Northern Mariana Islands",
39833         "mp",
39834         "1670"
39835       ],
39836       [
39837         "Norway (Norge)",
39838         "no",
39839         "47",
39840         0
39841       ],
39842       [
39843         "Oman (‫عُمان‬‎)",
39844         "om",
39845         "968"
39846       ],
39847       [
39848         "Pakistan (‫پاکستان‬‎)",
39849         "pk",
39850         "92"
39851       ],
39852       [
39853         "Palau",
39854         "pw",
39855         "680"
39856       ],
39857       [
39858         "Palestine (‫فلسطين‬‎)",
39859         "ps",
39860         "970"
39861       ],
39862       [
39863         "Panama (Panamá)",
39864         "pa",
39865         "507"
39866       ],
39867       [
39868         "Papua New Guinea",
39869         "pg",
39870         "675"
39871       ],
39872       [
39873         "Paraguay",
39874         "py",
39875         "595"
39876       ],
39877       [
39878         "Peru (Perú)",
39879         "pe",
39880         "51"
39881       ],
39882       [
39883         "Philippines",
39884         "ph",
39885         "63"
39886       ],
39887       [
39888         "Poland (Polska)",
39889         "pl",
39890         "48"
39891       ],
39892       [
39893         "Portugal",
39894         "pt",
39895         "351"
39896       ],
39897       [
39898         "Puerto Rico",
39899         "pr",
39900         "1",
39901         3,
39902         ["787", "939"]
39903       ],
39904       [
39905         "Qatar (‫قطر‬‎)",
39906         "qa",
39907         "974"
39908       ],
39909       [
39910         "Réunion (La Réunion)",
39911         "re",
39912         "262",
39913         0
39914       ],
39915       [
39916         "Romania (România)",
39917         "ro",
39918         "40"
39919       ],
39920       [
39921         "Russia (Россия)",
39922         "ru",
39923         "7",
39924         0
39925       ],
39926       [
39927         "Rwanda",
39928         "rw",
39929         "250"
39930       ],
39931       [
39932         "Saint Barthélemy",
39933         "bl",
39934         "590",
39935         1
39936       ],
39937       [
39938         "Saint Helena",
39939         "sh",
39940         "290"
39941       ],
39942       [
39943         "Saint Kitts and Nevis",
39944         "kn",
39945         "1869"
39946       ],
39947       [
39948         "Saint Lucia",
39949         "lc",
39950         "1758"
39951       ],
39952       [
39953         "Saint Martin (Saint-Martin (partie française))",
39954         "mf",
39955         "590",
39956         2
39957       ],
39958       [
39959         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39960         "pm",
39961         "508"
39962       ],
39963       [
39964         "Saint Vincent and the Grenadines",
39965         "vc",
39966         "1784"
39967       ],
39968       [
39969         "Samoa",
39970         "ws",
39971         "685"
39972       ],
39973       [
39974         "San Marino",
39975         "sm",
39976         "378"
39977       ],
39978       [
39979         "São Tomé and Príncipe (São Tomé e Príncipe)",
39980         "st",
39981         "239"
39982       ],
39983       [
39984         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39985         "sa",
39986         "966"
39987       ],
39988       [
39989         "Senegal (Sénégal)",
39990         "sn",
39991         "221"
39992       ],
39993       [
39994         "Serbia (Србија)",
39995         "rs",
39996         "381"
39997       ],
39998       [
39999         "Seychelles",
40000         "sc",
40001         "248"
40002       ],
40003       [
40004         "Sierra Leone",
40005         "sl",
40006         "232"
40007       ],
40008       [
40009         "Singapore",
40010         "sg",
40011         "65"
40012       ],
40013       [
40014         "Sint Maarten",
40015         "sx",
40016         "1721"
40017       ],
40018       [
40019         "Slovakia (Slovensko)",
40020         "sk",
40021         "421"
40022       ],
40023       [
40024         "Slovenia (Slovenija)",
40025         "si",
40026         "386"
40027       ],
40028       [
40029         "Solomon Islands",
40030         "sb",
40031         "677"
40032       ],
40033       [
40034         "Somalia (Soomaaliya)",
40035         "so",
40036         "252"
40037       ],
40038       [
40039         "South Africa",
40040         "za",
40041         "27"
40042       ],
40043       [
40044         "South Korea (대한민국)",
40045         "kr",
40046         "82"
40047       ],
40048       [
40049         "South Sudan (‫جنوب السودان‬‎)",
40050         "ss",
40051         "211"
40052       ],
40053       [
40054         "Spain (España)",
40055         "es",
40056         "34"
40057       ],
40058       [
40059         "Sri Lanka (ශ්‍රී ලංකාව)",
40060         "lk",
40061         "94"
40062       ],
40063       [
40064         "Sudan (‫السودان‬‎)",
40065         "sd",
40066         "249"
40067       ],
40068       [
40069         "Suriname",
40070         "sr",
40071         "597"
40072       ],
40073       [
40074         "Svalbard and Jan Mayen",
40075         "sj",
40076         "47",
40077         1
40078       ],
40079       [
40080         "Swaziland",
40081         "sz",
40082         "268"
40083       ],
40084       [
40085         "Sweden (Sverige)",
40086         "se",
40087         "46"
40088       ],
40089       [
40090         "Switzerland (Schweiz)",
40091         "ch",
40092         "41"
40093       ],
40094       [
40095         "Syria (‫سوريا‬‎)",
40096         "sy",
40097         "963"
40098       ],
40099       [
40100         "Taiwan (台灣)",
40101         "tw",
40102         "886"
40103       ],
40104       [
40105         "Tajikistan",
40106         "tj",
40107         "992"
40108       ],
40109       [
40110         "Tanzania",
40111         "tz",
40112         "255"
40113       ],
40114       [
40115         "Thailand (ไทย)",
40116         "th",
40117         "66"
40118       ],
40119       [
40120         "Timor-Leste",
40121         "tl",
40122         "670"
40123       ],
40124       [
40125         "Togo",
40126         "tg",
40127         "228"
40128       ],
40129       [
40130         "Tokelau",
40131         "tk",
40132         "690"
40133       ],
40134       [
40135         "Tonga",
40136         "to",
40137         "676"
40138       ],
40139       [
40140         "Trinidad and Tobago",
40141         "tt",
40142         "1868"
40143       ],
40144       [
40145         "Tunisia (‫تونس‬‎)",
40146         "tn",
40147         "216"
40148       ],
40149       [
40150         "Turkey (Türkiye)",
40151         "tr",
40152         "90"
40153       ],
40154       [
40155         "Turkmenistan",
40156         "tm",
40157         "993"
40158       ],
40159       [
40160         "Turks and Caicos Islands",
40161         "tc",
40162         "1649"
40163       ],
40164       [
40165         "Tuvalu",
40166         "tv",
40167         "688"
40168       ],
40169       [
40170         "U.S. Virgin Islands",
40171         "vi",
40172         "1340"
40173       ],
40174       [
40175         "Uganda",
40176         "ug",
40177         "256"
40178       ],
40179       [
40180         "Ukraine (Україна)",
40181         "ua",
40182         "380"
40183       ],
40184       [
40185         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40186         "ae",
40187         "971"
40188       ],
40189       [
40190         "United Kingdom",
40191         "gb",
40192         "44",
40193         0
40194       ],
40195       [
40196         "United States",
40197         "us",
40198         "1",
40199         0
40200       ],
40201       [
40202         "Uruguay",
40203         "uy",
40204         "598"
40205       ],
40206       [
40207         "Uzbekistan (Oʻzbekiston)",
40208         "uz",
40209         "998"
40210       ],
40211       [
40212         "Vanuatu",
40213         "vu",
40214         "678"
40215       ],
40216       [
40217         "Vatican City (Città del Vaticano)",
40218         "va",
40219         "39",
40220         1
40221       ],
40222       [
40223         "Venezuela",
40224         "ve",
40225         "58"
40226       ],
40227       [
40228         "Vietnam (Việt Nam)",
40229         "vn",
40230         "84"
40231       ],
40232       [
40233         "Wallis and Futuna (Wallis-et-Futuna)",
40234         "wf",
40235         "681"
40236       ],
40237       [
40238         "Western Sahara (‫الصحراء الغربية‬‎)",
40239         "eh",
40240         "212",
40241         1
40242       ],
40243       [
40244         "Yemen (‫اليمن‬‎)",
40245         "ye",
40246         "967"
40247       ],
40248       [
40249         "Zambia",
40250         "zm",
40251         "260"
40252       ],
40253       [
40254         "Zimbabwe",
40255         "zw",
40256         "263"
40257       ],
40258       [
40259         "Åland Islands",
40260         "ax",
40261         "358",
40262         1
40263       ]
40264   ];
40265   
40266   return d;
40267 }/**
40268 *    This script refer to:
40269 *    Title: International Telephone Input
40270 *    Author: Jack O'Connor
40271 *    Code version:  v12.1.12
40272 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40273 **/
40274
40275 /**
40276  * @class Roo.bootstrap.PhoneInput
40277  * @extends Roo.bootstrap.TriggerField
40278  * An input with International dial-code selection
40279  
40280  * @cfg {String} defaultDialCode default '+852'
40281  * @cfg {Array} preferedCountries default []
40282   
40283  * @constructor
40284  * Create a new PhoneInput.
40285  * @param {Object} config Configuration options
40286  */
40287
40288 Roo.bootstrap.PhoneInput = function(config) {
40289     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40290 };
40291
40292 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40293         
40294         listWidth: undefined,
40295         
40296         selectedClass: 'active',
40297         
40298         invalidClass : "has-warning",
40299         
40300         validClass: 'has-success',
40301         
40302         allowed: '0123456789',
40303         
40304         max_length: 15,
40305         
40306         /**
40307          * @cfg {String} defaultDialCode The default dial code when initializing the input
40308          */
40309         defaultDialCode: '+852',
40310         
40311         /**
40312          * @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
40313          */
40314         preferedCountries: false,
40315         
40316         getAutoCreate : function()
40317         {
40318             var data = Roo.bootstrap.PhoneInputData();
40319             var align = this.labelAlign || this.parentLabelAlign();
40320             var id = Roo.id();
40321             
40322             this.allCountries = [];
40323             this.dialCodeMapping = [];
40324             
40325             for (var i = 0; i < data.length; i++) {
40326               var c = data[i];
40327               this.allCountries[i] = {
40328                 name: c[0],
40329                 iso2: c[1],
40330                 dialCode: c[2],
40331                 priority: c[3] || 0,
40332                 areaCodes: c[4] || null
40333               };
40334               this.dialCodeMapping[c[2]] = {
40335                   name: c[0],
40336                   iso2: c[1],
40337                   priority: c[3] || 0,
40338                   areaCodes: c[4] || null
40339               };
40340             }
40341             
40342             var cfg = {
40343                 cls: 'form-group',
40344                 cn: []
40345             };
40346             
40347             var input =  {
40348                 tag: 'input',
40349                 id : id,
40350                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40351                 maxlength: this.max_length,
40352                 cls : 'form-control tel-input',
40353                 autocomplete: 'new-password'
40354             };
40355             
40356             var hiddenInput = {
40357                 tag: 'input',
40358                 type: 'hidden',
40359                 cls: 'hidden-tel-input'
40360             };
40361             
40362             if (this.name) {
40363                 hiddenInput.name = this.name;
40364             }
40365             
40366             if (this.disabled) {
40367                 input.disabled = true;
40368             }
40369             
40370             var flag_container = {
40371                 tag: 'div',
40372                 cls: 'flag-box',
40373                 cn: [
40374                     {
40375                         tag: 'div',
40376                         cls: 'flag'
40377                     },
40378                     {
40379                         tag: 'div',
40380                         cls: 'caret'
40381                     }
40382                 ]
40383             };
40384             
40385             var box = {
40386                 tag: 'div',
40387                 cls: this.hasFeedback ? 'has-feedback' : '',
40388                 cn: [
40389                     hiddenInput,
40390                     input,
40391                     {
40392                         tag: 'input',
40393                         cls: 'dial-code-holder',
40394                         disabled: true
40395                     }
40396                 ]
40397             };
40398             
40399             var container = {
40400                 cls: 'roo-select2-container input-group',
40401                 cn: [
40402                     flag_container,
40403                     box
40404                 ]
40405             };
40406             
40407             if (this.fieldLabel.length) {
40408                 var indicator = {
40409                     tag: 'i',
40410                     tooltip: 'This field is required'
40411                 };
40412                 
40413                 var label = {
40414                     tag: 'label',
40415                     'for':  id,
40416                     cls: 'control-label',
40417                     cn: []
40418                 };
40419                 
40420                 var label_text = {
40421                     tag: 'span',
40422                     html: this.fieldLabel
40423                 };
40424                 
40425                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40426                 label.cn = [
40427                     indicator,
40428                     label_text
40429                 ];
40430                 
40431                 if(this.indicatorpos == 'right') {
40432                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40433                     label.cn = [
40434                         label_text,
40435                         indicator
40436                     ];
40437                 }
40438                 
40439                 if(align == 'left') {
40440                     container = {
40441                         tag: 'div',
40442                         cn: [
40443                             container
40444                         ]
40445                     };
40446                     
40447                     if(this.labelWidth > 12){
40448                         label.style = "width: " + this.labelWidth + 'px';
40449                     }
40450                     if(this.labelWidth < 13 && this.labelmd == 0){
40451                         this.labelmd = this.labelWidth;
40452                     }
40453                     if(this.labellg > 0){
40454                         label.cls += ' col-lg-' + this.labellg;
40455                         input.cls += ' col-lg-' + (12 - this.labellg);
40456                     }
40457                     if(this.labelmd > 0){
40458                         label.cls += ' col-md-' + this.labelmd;
40459                         container.cls += ' col-md-' + (12 - this.labelmd);
40460                     }
40461                     if(this.labelsm > 0){
40462                         label.cls += ' col-sm-' + this.labelsm;
40463                         container.cls += ' col-sm-' + (12 - this.labelsm);
40464                     }
40465                     if(this.labelxs > 0){
40466                         label.cls += ' col-xs-' + this.labelxs;
40467                         container.cls += ' col-xs-' + (12 - this.labelxs);
40468                     }
40469                 }
40470             }
40471             
40472             cfg.cn = [
40473                 label,
40474                 container
40475             ];
40476             
40477             var settings = this;
40478             
40479             ['xs','sm','md','lg'].map(function(size){
40480                 if (settings[size]) {
40481                     cfg.cls += ' col-' + size + '-' + settings[size];
40482                 }
40483             });
40484             
40485             this.store = new Roo.data.Store({
40486                 proxy : new Roo.data.MemoryProxy({}),
40487                 reader : new Roo.data.JsonReader({
40488                     fields : [
40489                         {
40490                             'name' : 'name',
40491                             'type' : 'string'
40492                         },
40493                         {
40494                             'name' : 'iso2',
40495                             'type' : 'string'
40496                         },
40497                         {
40498                             'name' : 'dialCode',
40499                             'type' : 'string'
40500                         },
40501                         {
40502                             'name' : 'priority',
40503                             'type' : 'string'
40504                         },
40505                         {
40506                             'name' : 'areaCodes',
40507                             'type' : 'string'
40508                         }
40509                     ]
40510                 })
40511             });
40512             
40513             if(!this.preferedCountries) {
40514                 this.preferedCountries = [
40515                     'hk',
40516                     'gb',
40517                     'us'
40518                 ];
40519             }
40520             
40521             var p = this.preferedCountries.reverse();
40522             
40523             if(p) {
40524                 for (var i = 0; i < p.length; i++) {
40525                     for (var j = 0; j < this.allCountries.length; j++) {
40526                         if(this.allCountries[j].iso2 == p[i]) {
40527                             var t = this.allCountries[j];
40528                             this.allCountries.splice(j,1);
40529                             this.allCountries.unshift(t);
40530                         }
40531                     } 
40532                 }
40533             }
40534             
40535             this.store.proxy.data = {
40536                 success: true,
40537                 data: this.allCountries
40538             };
40539             
40540             return cfg;
40541         },
40542         
40543         initEvents : function()
40544         {
40545             this.createList();
40546             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40547             
40548             this.indicator = this.indicatorEl();
40549             this.flag = this.flagEl();
40550             this.dialCodeHolder = this.dialCodeHolderEl();
40551             
40552             this.trigger = this.el.select('div.flag-box',true).first();
40553             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40554             
40555             var _this = this;
40556             
40557             (function(){
40558                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40559                 _this.list.setWidth(lw);
40560             }).defer(100);
40561             
40562             this.list.on('mouseover', this.onViewOver, this);
40563             this.list.on('mousemove', this.onViewMove, this);
40564             this.inputEl().on("keyup", this.onKeyUp, this);
40565             this.inputEl().on("keypress", this.onKeyPress, this);
40566             
40567             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40568
40569             this.view = new Roo.View(this.list, this.tpl, {
40570                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40571             });
40572             
40573             this.view.on('click', this.onViewClick, this);
40574             this.setValue(this.defaultDialCode);
40575         },
40576         
40577         onTriggerClick : function(e)
40578         {
40579             Roo.log('trigger click');
40580             if(this.disabled){
40581                 return;
40582             }
40583             
40584             if(this.isExpanded()){
40585                 this.collapse();
40586                 this.hasFocus = false;
40587             }else {
40588                 this.store.load({});
40589                 this.hasFocus = true;
40590                 this.expand();
40591             }
40592         },
40593         
40594         isExpanded : function()
40595         {
40596             return this.list.isVisible();
40597         },
40598         
40599         collapse : function()
40600         {
40601             if(!this.isExpanded()){
40602                 return;
40603             }
40604             this.list.hide();
40605             Roo.get(document).un('mousedown', this.collapseIf, this);
40606             Roo.get(document).un('mousewheel', this.collapseIf, this);
40607             this.fireEvent('collapse', this);
40608             this.validate();
40609         },
40610         
40611         expand : function()
40612         {
40613             Roo.log('expand');
40614
40615             if(this.isExpanded() || !this.hasFocus){
40616                 return;
40617             }
40618             
40619             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40620             this.list.setWidth(lw);
40621             
40622             this.list.show();
40623             this.restrictHeight();
40624             
40625             Roo.get(document).on('mousedown', this.collapseIf, this);
40626             Roo.get(document).on('mousewheel', this.collapseIf, this);
40627             
40628             this.fireEvent('expand', this);
40629         },
40630         
40631         restrictHeight : function()
40632         {
40633             this.list.alignTo(this.inputEl(), this.listAlign);
40634             this.list.alignTo(this.inputEl(), this.listAlign);
40635         },
40636         
40637         onViewOver : function(e, t)
40638         {
40639             if(this.inKeyMode){
40640                 return;
40641             }
40642             var item = this.view.findItemFromChild(t);
40643             
40644             if(item){
40645                 var index = this.view.indexOf(item);
40646                 this.select(index, false);
40647             }
40648         },
40649
40650         // private
40651         onViewClick : function(view, doFocus, el, e)
40652         {
40653             var index = this.view.getSelectedIndexes()[0];
40654             
40655             var r = this.store.getAt(index);
40656             
40657             if(r){
40658                 this.onSelect(r, index);
40659             }
40660             if(doFocus !== false && !this.blockFocus){
40661                 this.inputEl().focus();
40662             }
40663         },
40664         
40665         onViewMove : function(e, t)
40666         {
40667             this.inKeyMode = false;
40668         },
40669         
40670         select : function(index, scrollIntoView)
40671         {
40672             this.selectedIndex = index;
40673             this.view.select(index);
40674             if(scrollIntoView !== false){
40675                 var el = this.view.getNode(index);
40676                 if(el){
40677                     this.list.scrollChildIntoView(el, false);
40678                 }
40679             }
40680         },
40681         
40682         createList : function()
40683         {
40684             this.list = Roo.get(document.body).createChild({
40685                 tag: 'ul',
40686                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40687                 style: 'display:none'
40688             });
40689             
40690             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40691         },
40692         
40693         collapseIf : function(e)
40694         {
40695             var in_combo  = e.within(this.el);
40696             var in_list =  e.within(this.list);
40697             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40698             
40699             if (in_combo || in_list || is_list) {
40700                 return;
40701             }
40702             this.collapse();
40703         },
40704         
40705         onSelect : function(record, index)
40706         {
40707             if(this.fireEvent('beforeselect', this, record, index) !== false){
40708                 
40709                 this.setFlagClass(record.data.iso2);
40710                 this.setDialCode(record.data.dialCode);
40711                 this.hasFocus = false;
40712                 this.collapse();
40713                 this.fireEvent('select', this, record, index);
40714             }
40715         },
40716         
40717         flagEl : function()
40718         {
40719             var flag = this.el.select('div.flag',true).first();
40720             if(!flag){
40721                 return false;
40722             }
40723             return flag;
40724         },
40725         
40726         dialCodeHolderEl : function()
40727         {
40728             var d = this.el.select('input.dial-code-holder',true).first();
40729             if(!d){
40730                 return false;
40731             }
40732             return d;
40733         },
40734         
40735         setDialCode : function(v)
40736         {
40737             this.dialCodeHolder.dom.value = '+'+v;
40738         },
40739         
40740         setFlagClass : function(n)
40741         {
40742             this.flag.dom.className = 'flag '+n;
40743         },
40744         
40745         getValue : function()
40746         {
40747             var v = this.inputEl().getValue();
40748             if(this.dialCodeHolder) {
40749                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40750             }
40751             return v;
40752         },
40753         
40754         setValue : function(v)
40755         {
40756             var d = this.getDialCode(v);
40757             
40758             //invalid dial code
40759             if(v.length == 0 || !d || d.length == 0) {
40760                 if(this.rendered){
40761                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40762                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40763                 }
40764                 return;
40765             }
40766             
40767             //valid dial code
40768             this.setFlagClass(this.dialCodeMapping[d].iso2);
40769             this.setDialCode(d);
40770             this.inputEl().dom.value = v.replace('+'+d,'');
40771             this.hiddenEl().dom.value = this.getValue();
40772             
40773             this.validate();
40774         },
40775         
40776         getDialCode : function(v)
40777         {
40778             v = v ||  '';
40779             
40780             if (v.length == 0) {
40781                 return this.dialCodeHolder.dom.value;
40782             }
40783             
40784             var dialCode = "";
40785             if (v.charAt(0) != "+") {
40786                 return false;
40787             }
40788             var numericChars = "";
40789             for (var i = 1; i < v.length; i++) {
40790               var c = v.charAt(i);
40791               if (!isNaN(c)) {
40792                 numericChars += c;
40793                 if (this.dialCodeMapping[numericChars]) {
40794                   dialCode = v.substr(1, i);
40795                 }
40796                 if (numericChars.length == 4) {
40797                   break;
40798                 }
40799               }
40800             }
40801             return dialCode;
40802         },
40803         
40804         reset : function()
40805         {
40806             this.setValue(this.defaultDialCode);
40807             this.validate();
40808         },
40809         
40810         hiddenEl : function()
40811         {
40812             return this.el.select('input.hidden-tel-input',true).first();
40813         },
40814         
40815         // after setting val
40816         onKeyUp : function(e){
40817             this.setValue(this.getValue());
40818         },
40819         
40820         onKeyPress : function(e){
40821             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40822                 e.stopEvent();
40823             }
40824         }
40825         
40826 });
40827 /**
40828  * @class Roo.bootstrap.MoneyField
40829  * @extends Roo.bootstrap.ComboBox
40830  * Bootstrap MoneyField class
40831  * 
40832  * @constructor
40833  * Create a new MoneyField.
40834  * @param {Object} config Configuration options
40835  */
40836
40837 Roo.bootstrap.MoneyField = function(config) {
40838     
40839     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40840     
40841 };
40842
40843 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40844     
40845     /**
40846      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40847      */
40848     allowDecimals : true,
40849     /**
40850      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40851      */
40852     decimalSeparator : ".",
40853     /**
40854      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40855      */
40856     decimalPrecision : 0,
40857     /**
40858      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40859      */
40860     allowNegative : true,
40861     /**
40862      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40863      */
40864     allowZero: true,
40865     /**
40866      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40867      */
40868     minValue : Number.NEGATIVE_INFINITY,
40869     /**
40870      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40871      */
40872     maxValue : Number.MAX_VALUE,
40873     /**
40874      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40875      */
40876     minText : "The minimum value for this field is {0}",
40877     /**
40878      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40879      */
40880     maxText : "The maximum value for this field is {0}",
40881     /**
40882      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40883      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40884      */
40885     nanText : "{0} is not a valid number",
40886     /**
40887      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40888      */
40889     castInt : true,
40890     /**
40891      * @cfg {String} defaults currency of the MoneyField
40892      * value should be in lkey
40893      */
40894     defaultCurrency : false,
40895     /**
40896      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40897      */
40898     thousandsDelimiter : false,
40899     /**
40900      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40901      */
40902     max_length: false,
40903     
40904     inputlg : 9,
40905     inputmd : 9,
40906     inputsm : 9,
40907     inputxs : 6,
40908     
40909     store : false,
40910     
40911     getAutoCreate : function()
40912     {
40913         var align = this.labelAlign || this.parentLabelAlign();
40914         
40915         var id = Roo.id();
40916
40917         var cfg = {
40918             cls: 'form-group',
40919             cn: []
40920         };
40921
40922         var input =  {
40923             tag: 'input',
40924             id : id,
40925             cls : 'form-control roo-money-amount-input',
40926             autocomplete: 'new-password'
40927         };
40928         
40929         var hiddenInput = {
40930             tag: 'input',
40931             type: 'hidden',
40932             id: Roo.id(),
40933             cls: 'hidden-number-input'
40934         };
40935         
40936         if(this.max_length) {
40937             input.maxlength = this.max_length; 
40938         }
40939         
40940         if (this.name) {
40941             hiddenInput.name = this.name;
40942         }
40943
40944         if (this.disabled) {
40945             input.disabled = true;
40946         }
40947
40948         var clg = 12 - this.inputlg;
40949         var cmd = 12 - this.inputmd;
40950         var csm = 12 - this.inputsm;
40951         var cxs = 12 - this.inputxs;
40952         
40953         var container = {
40954             tag : 'div',
40955             cls : 'row roo-money-field',
40956             cn : [
40957                 {
40958                     tag : 'div',
40959                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40960                     cn : [
40961                         {
40962                             tag : 'div',
40963                             cls: 'roo-select2-container input-group',
40964                             cn: [
40965                                 {
40966                                     tag : 'input',
40967                                     cls : 'form-control roo-money-currency-input',
40968                                     autocomplete: 'new-password',
40969                                     readOnly : 1,
40970                                     name : this.currencyName
40971                                 },
40972                                 {
40973                                     tag :'span',
40974                                     cls : 'input-group-addon',
40975                                     cn : [
40976                                         {
40977                                             tag: 'span',
40978                                             cls: 'caret'
40979                                         }
40980                                     ]
40981                                 }
40982                             ]
40983                         }
40984                     ]
40985                 },
40986                 {
40987                     tag : 'div',
40988                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40989                     cn : [
40990                         {
40991                             tag: 'div',
40992                             cls: this.hasFeedback ? 'has-feedback' : '',
40993                             cn: [
40994                                 input
40995                             ]
40996                         }
40997                     ]
40998                 }
40999             ]
41000             
41001         };
41002         
41003         if (this.fieldLabel.length) {
41004             var indicator = {
41005                 tag: 'i',
41006                 tooltip: 'This field is required'
41007             };
41008
41009             var label = {
41010                 tag: 'label',
41011                 'for':  id,
41012                 cls: 'control-label',
41013                 cn: []
41014             };
41015
41016             var label_text = {
41017                 tag: 'span',
41018                 html: this.fieldLabel
41019             };
41020
41021             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41022             label.cn = [
41023                 indicator,
41024                 label_text
41025             ];
41026
41027             if(this.indicatorpos == 'right') {
41028                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41029                 label.cn = [
41030                     label_text,
41031                     indicator
41032                 ];
41033             }
41034
41035             if(align == 'left') {
41036                 container = {
41037                     tag: 'div',
41038                     cn: [
41039                         container
41040                     ]
41041                 };
41042
41043                 if(this.labelWidth > 12){
41044                     label.style = "width: " + this.labelWidth + 'px';
41045                 }
41046                 if(this.labelWidth < 13 && this.labelmd == 0){
41047                     this.labelmd = this.labelWidth;
41048                 }
41049                 if(this.labellg > 0){
41050                     label.cls += ' col-lg-' + this.labellg;
41051                     input.cls += ' col-lg-' + (12 - this.labellg);
41052                 }
41053                 if(this.labelmd > 0){
41054                     label.cls += ' col-md-' + this.labelmd;
41055                     container.cls += ' col-md-' + (12 - this.labelmd);
41056                 }
41057                 if(this.labelsm > 0){
41058                     label.cls += ' col-sm-' + this.labelsm;
41059                     container.cls += ' col-sm-' + (12 - this.labelsm);
41060                 }
41061                 if(this.labelxs > 0){
41062                     label.cls += ' col-xs-' + this.labelxs;
41063                     container.cls += ' col-xs-' + (12 - this.labelxs);
41064                 }
41065             }
41066         }
41067
41068         cfg.cn = [
41069             label,
41070             container,
41071             hiddenInput
41072         ];
41073         
41074         var settings = this;
41075
41076         ['xs','sm','md','lg'].map(function(size){
41077             if (settings[size]) {
41078                 cfg.cls += ' col-' + size + '-' + settings[size];
41079             }
41080         });
41081         
41082         return cfg;
41083     },
41084     
41085     initEvents : function()
41086     {
41087         this.indicator = this.indicatorEl();
41088         
41089         this.initCurrencyEvent();
41090         
41091         this.initNumberEvent();
41092     },
41093     
41094     initCurrencyEvent : function()
41095     {
41096         if (!this.store) {
41097             throw "can not find store for combo";
41098         }
41099         
41100         this.store = Roo.factory(this.store, Roo.data);
41101         this.store.parent = this;
41102         
41103         this.createList();
41104         
41105         this.triggerEl = this.el.select('.input-group-addon', true).first();
41106         
41107         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41108         
41109         var _this = this;
41110         
41111         (function(){
41112             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41113             _this.list.setWidth(lw);
41114         }).defer(100);
41115         
41116         this.list.on('mouseover', this.onViewOver, this);
41117         this.list.on('mousemove', this.onViewMove, this);
41118         this.list.on('scroll', this.onViewScroll, this);
41119         
41120         if(!this.tpl){
41121             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41122         }
41123         
41124         this.view = new Roo.View(this.list, this.tpl, {
41125             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41126         });
41127         
41128         this.view.on('click', this.onViewClick, this);
41129         
41130         this.store.on('beforeload', this.onBeforeLoad, this);
41131         this.store.on('load', this.onLoad, this);
41132         this.store.on('loadexception', this.onLoadException, this);
41133         
41134         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41135             "up" : function(e){
41136                 this.inKeyMode = true;
41137                 this.selectPrev();
41138             },
41139
41140             "down" : function(e){
41141                 if(!this.isExpanded()){
41142                     this.onTriggerClick();
41143                 }else{
41144                     this.inKeyMode = true;
41145                     this.selectNext();
41146                 }
41147             },
41148
41149             "enter" : function(e){
41150                 this.collapse();
41151                 
41152                 if(this.fireEvent("specialkey", this, e)){
41153                     this.onViewClick(false);
41154                 }
41155                 
41156                 return true;
41157             },
41158
41159             "esc" : function(e){
41160                 this.collapse();
41161             },
41162
41163             "tab" : function(e){
41164                 this.collapse();
41165                 
41166                 if(this.fireEvent("specialkey", this, e)){
41167                     this.onViewClick(false);
41168                 }
41169                 
41170                 return true;
41171             },
41172
41173             scope : this,
41174
41175             doRelay : function(foo, bar, hname){
41176                 if(hname == 'down' || this.scope.isExpanded()){
41177                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41178                 }
41179                 return true;
41180             },
41181
41182             forceKeyDown: true
41183         });
41184         
41185         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41186         
41187     },
41188     
41189     initNumberEvent : function(e)
41190     {
41191         this.inputEl().on("keydown" , this.fireKey,  this);
41192         this.inputEl().on("focus", this.onFocus,  this);
41193         this.inputEl().on("blur", this.onBlur,  this);
41194         
41195         this.inputEl().relayEvent('keyup', this);
41196         
41197         if(this.indicator){
41198             this.indicator.addClass('invisible');
41199         }
41200  
41201         this.originalValue = this.getValue();
41202         
41203         if(this.validationEvent == 'keyup'){
41204             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41205             this.inputEl().on('keyup', this.filterValidation, this);
41206         }
41207         else if(this.validationEvent !== false){
41208             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41209         }
41210         
41211         if(this.selectOnFocus){
41212             this.on("focus", this.preFocus, this);
41213             
41214         }
41215         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41216             this.inputEl().on("keypress", this.filterKeys, this);
41217         } else {
41218             this.inputEl().relayEvent('keypress', this);
41219         }
41220         
41221         var allowed = "0123456789";
41222         
41223         if(this.allowDecimals){
41224             allowed += this.decimalSeparator;
41225         }
41226         
41227         if(this.allowNegative){
41228             allowed += "-";
41229         }
41230         
41231         if(this.thousandsDelimiter) {
41232             allowed += ",";
41233         }
41234         
41235         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41236         
41237         var keyPress = function(e){
41238             
41239             var k = e.getKey();
41240             
41241             var c = e.getCharCode();
41242             
41243             if(
41244                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41245                     allowed.indexOf(String.fromCharCode(c)) === -1
41246             ){
41247                 e.stopEvent();
41248                 return;
41249             }
41250             
41251             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41252                 return;
41253             }
41254             
41255             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41256                 e.stopEvent();
41257             }
41258         };
41259         
41260         this.inputEl().on("keypress", keyPress, this);
41261         
41262     },
41263     
41264     onTriggerClick : function(e)
41265     {   
41266         if(this.disabled){
41267             return;
41268         }
41269         
41270         this.page = 0;
41271         this.loadNext = false;
41272         
41273         if(this.isExpanded()){
41274             this.collapse();
41275             return;
41276         }
41277         
41278         this.hasFocus = true;
41279         
41280         if(this.triggerAction == 'all') {
41281             this.doQuery(this.allQuery, true);
41282             return;
41283         }
41284         
41285         this.doQuery(this.getRawValue());
41286     },
41287     
41288     getCurrency : function()
41289     {   
41290         var v = this.currencyEl().getValue();
41291         
41292         return v;
41293     },
41294     
41295     restrictHeight : function()
41296     {
41297         this.list.alignTo(this.currencyEl(), this.listAlign);
41298         this.list.alignTo(this.currencyEl(), this.listAlign);
41299     },
41300     
41301     onViewClick : function(view, doFocus, el, e)
41302     {
41303         var index = this.view.getSelectedIndexes()[0];
41304         
41305         var r = this.store.getAt(index);
41306         
41307         if(r){
41308             this.onSelect(r, index);
41309         }
41310     },
41311     
41312     onSelect : function(record, index){
41313         
41314         if(this.fireEvent('beforeselect', this, record, index) !== false){
41315         
41316             this.setFromCurrencyData(index > -1 ? record.data : false);
41317             
41318             this.collapse();
41319             
41320             this.fireEvent('select', this, record, index);
41321         }
41322     },
41323     
41324     setFromCurrencyData : function(o)
41325     {
41326         var currency = '';
41327         
41328         this.lastCurrency = o;
41329         
41330         if (this.currencyField) {
41331             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41332         } else {
41333             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41334         }
41335         
41336         this.lastSelectionText = currency;
41337         
41338         //setting default currency
41339         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41340             this.setCurrency(this.defaultCurrency);
41341             return;
41342         }
41343         
41344         this.setCurrency(currency);
41345     },
41346     
41347     setFromData : function(o)
41348     {
41349         var c = {};
41350         
41351         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41352         
41353         this.setFromCurrencyData(c);
41354         
41355         var value = '';
41356         
41357         if (this.name) {
41358             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41359         } else {
41360             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41361         }
41362         
41363         this.setValue(value);
41364         
41365     },
41366     
41367     setCurrency : function(v)
41368     {   
41369         this.currencyValue = v;
41370         
41371         if(this.rendered){
41372             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41373             this.validate();
41374         }
41375     },
41376     
41377     setValue : function(v)
41378     {
41379         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41380         
41381         this.value = v;
41382         
41383         if(this.rendered){
41384             
41385             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41386             
41387             this.inputEl().dom.value = (v == '') ? '' :
41388                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41389             
41390             if(!this.allowZero && v === '0') {
41391                 this.hiddenEl().dom.value = '';
41392                 this.inputEl().dom.value = '';
41393             }
41394             
41395             this.validate();
41396         }
41397     },
41398     
41399     getRawValue : function()
41400     {
41401         var v = this.inputEl().getValue();
41402         
41403         return v;
41404     },
41405     
41406     getValue : function()
41407     {
41408         return this.fixPrecision(this.parseValue(this.getRawValue()));
41409     },
41410     
41411     parseValue : function(value)
41412     {
41413         if(this.thousandsDelimiter) {
41414             value += "";
41415             r = new RegExp(",", "g");
41416             value = value.replace(r, "");
41417         }
41418         
41419         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41420         return isNaN(value) ? '' : value;
41421         
41422     },
41423     
41424     fixPrecision : function(value)
41425     {
41426         if(this.thousandsDelimiter) {
41427             value += "";
41428             r = new RegExp(",", "g");
41429             value = value.replace(r, "");
41430         }
41431         
41432         var nan = isNaN(value);
41433         
41434         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41435             return nan ? '' : value;
41436         }
41437         return parseFloat(value).toFixed(this.decimalPrecision);
41438     },
41439     
41440     decimalPrecisionFcn : function(v)
41441     {
41442         return Math.floor(v);
41443     },
41444     
41445     validateValue : function(value)
41446     {
41447         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41448             return false;
41449         }
41450         
41451         var num = this.parseValue(value);
41452         
41453         if(isNaN(num)){
41454             this.markInvalid(String.format(this.nanText, value));
41455             return false;
41456         }
41457         
41458         if(num < this.minValue){
41459             this.markInvalid(String.format(this.minText, this.minValue));
41460             return false;
41461         }
41462         
41463         if(num > this.maxValue){
41464             this.markInvalid(String.format(this.maxText, this.maxValue));
41465             return false;
41466         }
41467         
41468         return true;
41469     },
41470     
41471     validate : function()
41472     {
41473         if(this.disabled || this.allowBlank){
41474             this.markValid();
41475             return true;
41476         }
41477         
41478         var currency = this.getCurrency();
41479         
41480         if(this.validateValue(this.getRawValue()) && currency.length){
41481             this.markValid();
41482             return true;
41483         }
41484         
41485         this.markInvalid();
41486         return false;
41487     },
41488     
41489     getName: function()
41490     {
41491         return this.name;
41492     },
41493     
41494     beforeBlur : function()
41495     {
41496         if(!this.castInt){
41497             return;
41498         }
41499         
41500         var v = this.parseValue(this.getRawValue());
41501         
41502         if(v || v == 0){
41503             this.setValue(v);
41504         }
41505     },
41506     
41507     onBlur : function()
41508     {
41509         this.beforeBlur();
41510         
41511         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41512             //this.el.removeClass(this.focusClass);
41513         }
41514         
41515         this.hasFocus = false;
41516         
41517         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41518             this.validate();
41519         }
41520         
41521         var v = this.getValue();
41522         
41523         if(String(v) !== String(this.startValue)){
41524             this.fireEvent('change', this, v, this.startValue);
41525         }
41526         
41527         this.fireEvent("blur", this);
41528     },
41529     
41530     inputEl : function()
41531     {
41532         return this.el.select('.roo-money-amount-input', true).first();
41533     },
41534     
41535     currencyEl : function()
41536     {
41537         return this.el.select('.roo-money-currency-input', true).first();
41538     },
41539     
41540     hiddenEl : function()
41541     {
41542         return this.el.select('input.hidden-number-input',true).first();
41543     }
41544     
41545 });/**
41546  * @class Roo.bootstrap.BezierSignature
41547  * @extends Roo.bootstrap.Component
41548  * Bootstrap BezierSignature class
41549  * This script refer to:
41550  *    Title: Signature Pad
41551  *    Author: szimek
41552  *    Availability: https://github.com/szimek/signature_pad
41553  *
41554  * @constructor
41555  * Create a new BezierSignature
41556  * @param {Object} config The config object
41557  */
41558
41559 Roo.bootstrap.BezierSignature = function(config){
41560     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41561     this.addEvents({
41562         "resize" : true
41563     });
41564 };
41565
41566 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41567 {
41568      
41569     curve_data: [],
41570     
41571     is_empty: true,
41572     
41573     mouse_btn_down: true,
41574     
41575     /**
41576      * @cfg {int} canvas height
41577      */
41578     canvas_height: '200px',
41579     
41580     /**
41581      * @cfg {float|function} Radius of a single dot.
41582      */ 
41583     dot_size: false,
41584     
41585     /**
41586      * @cfg {float} Minimum width of a line. Defaults to 0.5.
41587      */
41588     min_width: 0.5,
41589     
41590     /**
41591      * @cfg {float} Maximum width of a line. Defaults to 2.5.
41592      */
41593     max_width: 2.5,
41594     
41595     /**
41596      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41597      */
41598     throttle: 16,
41599     
41600     /**
41601      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41602      */
41603     min_distance: 5,
41604     
41605     /**
41606      * @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.
41607      */
41608     bg_color: 'rgba(0, 0, 0, 0)',
41609     
41610     /**
41611      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41612      */
41613     dot_color: 'black',
41614     
41615     /**
41616      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41617      */ 
41618     velocity_filter_weight: 0.7,
41619     
41620     /**
41621      * @cfg {function} Callback when stroke begin. 
41622      */
41623     onBegin: false,
41624     
41625     /**
41626      * @cfg {function} Callback when stroke end.
41627      */
41628     onEnd: false,
41629     
41630     getAutoCreate : function()
41631     {
41632         var cls = 'roo-signature column';
41633         
41634         if(this.cls){
41635             cls += ' ' + this.cls;
41636         }
41637         
41638         var col_sizes = [
41639             'lg',
41640             'md',
41641             'sm',
41642             'xs'
41643         ];
41644         
41645         for(var i = 0; i < col_sizes.length; i++) {
41646             if(this[col_sizes[i]]) {
41647                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
41648             }
41649         }
41650         
41651         var cfg = {
41652             tag: 'div',
41653             cls: cls,
41654             cn: [
41655                 {
41656                     tag: 'div',
41657                     cls: 'roo-signature-body',
41658                     cn: [
41659                         {
41660                             tag: 'canvas',
41661                             cls: 'roo-signature-body-canvas',
41662                             height: this.canvas_height,
41663                             width: this.canvas_width
41664                         }
41665                     ]
41666                 },
41667                 {
41668                     tag: 'input',
41669                     type: 'file',
41670                     style: 'display: none'
41671                 }
41672             ]
41673         };
41674         
41675         return cfg;
41676     },
41677     
41678     initEvents: function() 
41679     {
41680         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
41681         
41682         var canvas = this.canvasEl();
41683         
41684         // mouse && touch event swapping...
41685         canvas.dom.style.touchAction = 'none';
41686         canvas.dom.style.msTouchAction = 'none';
41687         
41688         this.mouse_btn_down = false;
41689         canvas.on('mousedown', this._handleMouseDown, this);
41690         canvas.on('mousemove', this._handleMouseMove, this);
41691         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
41692         
41693         if (window.PointerEvent) {
41694             canvas.on('pointerdown', this._handleMouseDown, this);
41695             canvas.on('pointermove', this._handleMouseMove, this);
41696             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
41697         }
41698         
41699         if ('ontouchstart' in window) {
41700             canvas.on('touchstart', this._handleTouchStart, this);
41701             canvas.on('touchmove', this._handleTouchMove, this);
41702             canvas.on('touchend', this._handleTouchEnd, this);
41703         }
41704         
41705         Roo.EventManager.onWindowResize(this.resize, this, true);
41706         
41707         // file input event
41708         this.fileEl().on('change', this.uploadImage, this);
41709         
41710         this.clear();
41711         
41712         this.resize();
41713     },
41714     
41715     resize: function(){
41716         
41717         var canvas = this.canvasEl().dom;
41718         var ctx = this.canvasElCtx();
41719         var img_data = false;
41720         
41721         if(canvas.width > 0) {
41722             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
41723         }
41724         // setting canvas width will clean img data
41725         canvas.width = 0;
41726         
41727         var style = window.getComputedStyle ? 
41728             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
41729             
41730         var padding_left = parseInt(style.paddingLeft) || 0;
41731         var padding_right = parseInt(style.paddingRight) || 0;
41732         
41733         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
41734         
41735         if(img_data) {
41736             ctx.putImageData(img_data, 0, 0);
41737         }
41738     },
41739     
41740     _handleMouseDown: function(e)
41741     {
41742         if (e.browserEvent.which === 1) {
41743             this.mouse_btn_down = true;
41744             this.strokeBegin(e);
41745         }
41746     },
41747     
41748     _handleMouseMove: function (e)
41749     {
41750         if (this.mouse_btn_down) {
41751             this.strokeMoveUpdate(e);
41752         }
41753     },
41754     
41755     _handleMouseUp: function (e)
41756     {
41757         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
41758             this.mouse_btn_down = false;
41759             this.strokeEnd(e);
41760         }
41761     },
41762     
41763     _handleTouchStart: function (e) {
41764         
41765         e.preventDefault();
41766         if (e.browserEvent.targetTouches.length === 1) {
41767             // var touch = e.browserEvent.changedTouches[0];
41768             // this.strokeBegin(touch);
41769             
41770              this.strokeBegin(e); // assume e catching the correct xy...
41771         }
41772     },
41773     
41774     _handleTouchMove: function (e) {
41775         e.preventDefault();
41776         // var touch = event.targetTouches[0];
41777         // _this._strokeMoveUpdate(touch);
41778         this.strokeMoveUpdate(e);
41779     },
41780     
41781     _handleTouchEnd: function (e) {
41782         var wasCanvasTouched = e.target === this.canvasEl().dom;
41783         if (wasCanvasTouched) {
41784             e.preventDefault();
41785             // var touch = event.changedTouches[0];
41786             // _this._strokeEnd(touch);
41787             this.strokeEnd(e);
41788         }
41789     },
41790     
41791     reset: function () {
41792         this._lastPoints = [];
41793         this._lastVelocity = 0;
41794         this._lastWidth = (this.min_width + this.max_width) / 2;
41795         this.canvasElCtx().fillStyle = this.dot_color;
41796     },
41797     
41798     strokeMoveUpdate: function(e)
41799     {
41800         this.strokeUpdate(e);
41801         
41802         if (this.throttle) {
41803             this.throttleStroke(this.strokeUpdate, this.throttle);
41804         }
41805         else {
41806             this.strokeUpdate(e);
41807         }
41808     },
41809     
41810     strokeBegin: function(e)
41811     {
41812         var newPointGroup = {
41813             color: this.dot_color,
41814             points: []
41815         };
41816         
41817         if (typeof this.onBegin === 'function') {
41818             this.onBegin(e);
41819         }
41820         
41821         this.curve_data.push(newPointGroup);
41822         this.reset();
41823         this.strokeUpdate(e);
41824     },
41825     
41826     strokeUpdate: function(e)
41827     {
41828         var rect = this.canvasEl().dom.getBoundingClientRect();
41829         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
41830         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
41831         var lastPoints = lastPointGroup.points;
41832         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
41833         var isLastPointTooClose = lastPoint
41834             ? point.distanceTo(lastPoint) <= this.min_distance
41835             : false;
41836         var color = lastPointGroup.color;
41837         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
41838             var curve = this.addPoint(point);
41839             if (!lastPoint) {
41840                 this.drawDot({color: color, point: point});
41841             }
41842             else if (curve) {
41843                 this.drawCurve({color: color, curve: curve});
41844             }
41845             lastPoints.push({
41846                 time: point.time,
41847                 x: point.x,
41848                 y: point.y
41849             });
41850         }
41851     },
41852     
41853     strokeEnd: function(e)
41854     {
41855         this.strokeUpdate(e);
41856         if (typeof this.onEnd === 'function') {
41857             this.onEnd(e);
41858         }
41859     },
41860     
41861     addPoint:  function (point) {
41862         var _lastPoints = this._lastPoints;
41863         _lastPoints.push(point);
41864         if (_lastPoints.length > 2) {
41865             if (_lastPoints.length === 3) {
41866                 _lastPoints.unshift(_lastPoints[0]);
41867             }
41868             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
41869             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
41870             _lastPoints.shift();
41871             return curve;
41872         }
41873         return null;
41874     },
41875     
41876     calculateCurveWidths: function (startPoint, endPoint) {
41877         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
41878             (1 - this.velocity_filter_weight) * this._lastVelocity;
41879
41880         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
41881         var widths = {
41882             end: newWidth,
41883             start: this._lastWidth
41884         };
41885         
41886         this._lastVelocity = velocity;
41887         this._lastWidth = newWidth;
41888         return widths;
41889     },
41890     
41891     drawDot: function (_a) {
41892         var color = _a.color, point = _a.point;
41893         var ctx = this.canvasElCtx();
41894         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
41895         ctx.beginPath();
41896         this.drawCurveSegment(point.x, point.y, width);
41897         ctx.closePath();
41898         ctx.fillStyle = color;
41899         ctx.fill();
41900     },
41901     
41902     drawCurve: function (_a) {
41903         var color = _a.color, curve = _a.curve;
41904         var ctx = this.canvasElCtx();
41905         var widthDelta = curve.endWidth - curve.startWidth;
41906         var drawSteps = Math.floor(curve.length()) * 2;
41907         ctx.beginPath();
41908         ctx.fillStyle = color;
41909         for (var i = 0; i < drawSteps; i += 1) {
41910         var t = i / drawSteps;
41911         var tt = t * t;
41912         var ttt = tt * t;
41913         var u = 1 - t;
41914         var uu = u * u;
41915         var uuu = uu * u;
41916         var x = uuu * curve.startPoint.x;
41917         x += 3 * uu * t * curve.control1.x;
41918         x += 3 * u * tt * curve.control2.x;
41919         x += ttt * curve.endPoint.x;
41920         var y = uuu * curve.startPoint.y;
41921         y += 3 * uu * t * curve.control1.y;
41922         y += 3 * u * tt * curve.control2.y;
41923         y += ttt * curve.endPoint.y;
41924         var width = curve.startWidth + ttt * widthDelta;
41925         this.drawCurveSegment(x, y, width);
41926         }
41927         ctx.closePath();
41928         ctx.fill();
41929     },
41930     
41931     drawCurveSegment: function (x, y, width) {
41932         var ctx = this.canvasElCtx();
41933         ctx.moveTo(x, y);
41934         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
41935         this.is_empty = false;
41936     },
41937     
41938     clear: function()
41939     {
41940         var ctx = this.canvasElCtx();
41941         var canvas = this.canvasEl().dom;
41942         ctx.fillStyle = this.bg_color;
41943         ctx.clearRect(0, 0, canvas.width, canvas.height);
41944         ctx.fillRect(0, 0, canvas.width, canvas.height);
41945         this.curve_data = [];
41946         this.reset();
41947         this.is_empty = true;
41948     },
41949     
41950     fileEl: function()
41951     {
41952         return  this.el.select('input',true).first();
41953     },
41954     
41955     canvasEl: function()
41956     {
41957         return this.el.select('canvas',true).first();
41958     },
41959     
41960     canvasElCtx: function()
41961     {
41962         return this.el.select('canvas',true).first().dom.getContext('2d');
41963     },
41964     
41965     getImage: function(type)
41966     {
41967         if(this.is_empty) {
41968             return false;
41969         }
41970         
41971         // encryption ?
41972         return this.canvasEl().dom.toDataURL('image/'+type, 1);
41973     },
41974     
41975     drawFromImage: function(img_src)
41976     {
41977         var img = new Image();
41978         
41979         img.onload = function(){
41980             this.canvasElCtx().drawImage(img, 0, 0);
41981         }.bind(this);
41982         
41983         img.src = img_src;
41984         
41985         this.is_empty = false;
41986     },
41987     
41988     selectImage: function()
41989     {
41990         this.fileEl().dom.click();
41991     },
41992     
41993     uploadImage: function(e)
41994     {
41995         var reader = new FileReader();
41996         
41997         reader.onload = function(e){
41998             var img = new Image();
41999             img.onload = function(){
42000                 this.reset();
42001                 this.canvasElCtx().drawImage(img, 0, 0);
42002             }.bind(this);
42003             img.src = e.target.result;
42004         }.bind(this);
42005         
42006         reader.readAsDataURL(e.target.files[0]);
42007     },
42008     
42009     // Bezier Point Constructor
42010     Point: (function () {
42011         function Point(x, y, time) {
42012             this.x = x;
42013             this.y = y;
42014             this.time = time || Date.now();
42015         }
42016         Point.prototype.distanceTo = function (start) {
42017             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42018         };
42019         Point.prototype.equals = function (other) {
42020             return this.x === other.x && this.y === other.y && this.time === other.time;
42021         };
42022         Point.prototype.velocityFrom = function (start) {
42023             return this.time !== start.time
42024             ? this.distanceTo(start) / (this.time - start.time)
42025             : 0;
42026         };
42027         return Point;
42028     }()),
42029     
42030     
42031     // Bezier Constructor
42032     Bezier: (function () {
42033         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42034             this.startPoint = startPoint;
42035             this.control2 = control2;
42036             this.control1 = control1;
42037             this.endPoint = endPoint;
42038             this.startWidth = startWidth;
42039             this.endWidth = endWidth;
42040         }
42041         Bezier.fromPoints = function (points, widths, scope) {
42042             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42043             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42044             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42045         };
42046         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42047             var dx1 = s1.x - s2.x;
42048             var dy1 = s1.y - s2.y;
42049             var dx2 = s2.x - s3.x;
42050             var dy2 = s2.y - s3.y;
42051             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42052             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42053             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42054             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42055             var dxm = m1.x - m2.x;
42056             var dym = m1.y - m2.y;
42057             var k = l2 / (l1 + l2);
42058             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42059             var tx = s2.x - cm.x;
42060             var ty = s2.y - cm.y;
42061             return {
42062                 c1: new scope.Point(m1.x + tx, m1.y + ty),
42063                 c2: new scope.Point(m2.x + tx, m2.y + ty)
42064             };
42065         };
42066         Bezier.prototype.length = function () {
42067             var steps = 10;
42068             var length = 0;
42069             var px;
42070             var py;
42071             for (var i = 0; i <= steps; i += 1) {
42072                 var t = i / steps;
42073                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42074                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42075                 if (i > 0) {
42076                     var xdiff = cx - px;
42077                     var ydiff = cy - py;
42078                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42079                 }
42080                 px = cx;
42081                 py = cy;
42082             }
42083             return length;
42084         };
42085         Bezier.prototype.point = function (t, start, c1, c2, end) {
42086             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42087             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42088             + (3.0 * c2 * (1.0 - t) * t * t)
42089             + (end * t * t * t);
42090         };
42091         return Bezier;
42092     }()),
42093     
42094     throttleStroke: function(fn, wait) {
42095       if (wait === void 0) { wait = 250; }
42096       var previous = 0;
42097       var timeout = null;
42098       var result;
42099       var storedContext;
42100       var storedArgs;
42101       var later = function () {
42102           previous = Date.now();
42103           timeout = null;
42104           result = fn.apply(storedContext, storedArgs);
42105           if (!timeout) {
42106               storedContext = null;
42107               storedArgs = [];
42108           }
42109       };
42110       return function wrapper() {
42111           var args = [];
42112           for (var _i = 0; _i < arguments.length; _i++) {
42113               args[_i] = arguments[_i];
42114           }
42115           var now = Date.now();
42116           var remaining = wait - (now - previous);
42117           storedContext = this;
42118           storedArgs = args;
42119           if (remaining <= 0 || remaining > wait) {
42120               if (timeout) {
42121                   clearTimeout(timeout);
42122                   timeout = null;
42123               }
42124               previous = now;
42125               result = fn.apply(storedContext, storedArgs);
42126               if (!timeout) {
42127                   storedContext = null;
42128                   storedArgs = [];
42129               }
42130           }
42131           else if (!timeout) {
42132               timeout = window.setTimeout(later, remaining);
42133           }
42134           return result;
42135       };
42136   }
42137   
42138 });
42139
42140  
42141
42142