Roo/form/ComboNested.js
[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  && 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 + ' hidden' + size + '-down';;
1076                 return;
1077             }
1078             cfg.cls += ' col-' + size + '-' + settings[size] + (
1079                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1080             );
1081             
1082         });
1083         
1084         if (this.hidden) {
1085             cfg.cls += ' hidden';
1086         }
1087         
1088         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1089             cfg.cls +=' alert alert-' + this.alert;
1090         }
1091         
1092         
1093         if (this.html.length) {
1094             cfg.html = this.html;
1095         }
1096         if (this.fa) {
1097             var fasize = '';
1098             if (this.fasize > 1) {
1099                 fasize = ' fa-' + this.fasize + 'x';
1100             }
1101             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1102             
1103             
1104         }
1105         if (this.icon) {
1106             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1107         }
1108         
1109         return cfg;
1110     }
1111    
1112 });
1113
1114  
1115
1116  /*
1117  * - LGPL
1118  *
1119  * page container.
1120  * 
1121  */
1122
1123
1124 /**
1125  * @class Roo.bootstrap.Container
1126  * @extends Roo.bootstrap.Component
1127  * Bootstrap Container class
1128  * @cfg {Boolean} jumbotron is it a jumbotron element
1129  * @cfg {String} html content of element
1130  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1131  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1132  * @cfg {String} header content of header (for panel)
1133  * @cfg {String} footer content of footer (for panel)
1134  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1135  * @cfg {String} tag (header|aside|section) type of HTML tag.
1136  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1137  * @cfg {String} fa font awesome icon
1138  * @cfg {String} icon (info-sign|check|...) glyphicon name
1139  * @cfg {Boolean} hidden (true|false) hide the element
1140  * @cfg {Boolean} expandable (true|false) default false
1141  * @cfg {Boolean} expanded (true|false) default true
1142  * @cfg {String} rheader contet on the right of header
1143  * @cfg {Boolean} clickable (true|false) default false
1144
1145  *     
1146  * @constructor
1147  * Create a new Container
1148  * @param {Object} config The config object
1149  */
1150
1151 Roo.bootstrap.Container = function(config){
1152     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1153     
1154     this.addEvents({
1155         // raw events
1156          /**
1157          * @event expand
1158          * After the panel has been expand
1159          * 
1160          * @param {Roo.bootstrap.Container} this
1161          */
1162         "expand" : true,
1163         /**
1164          * @event collapse
1165          * After the panel has been collapsed
1166          * 
1167          * @param {Roo.bootstrap.Container} this
1168          */
1169         "collapse" : true,
1170         /**
1171          * @event click
1172          * When a element is chick
1173          * @param {Roo.bootstrap.Container} this
1174          * @param {Roo.EventObject} e
1175          */
1176         "click" : true
1177     });
1178 };
1179
1180 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1181     
1182     jumbotron : false,
1183     well: '',
1184     panel : '',
1185     header: '',
1186     footer : '',
1187     sticky: '',
1188     tag : false,
1189     alert : false,
1190     fa: false,
1191     icon : false,
1192     expandable : false,
1193     rheader : '',
1194     expanded : true,
1195     clickable: false,
1196   
1197      
1198     getChildContainer : function() {
1199         
1200         if(!this.el){
1201             return false;
1202         }
1203         
1204         if (this.panel.length) {
1205             return this.el.select('.panel-body',true).first();
1206         }
1207         
1208         return this.el;
1209     },
1210     
1211     
1212     getAutoCreate : function(){
1213         
1214         var cfg = {
1215             tag : this.tag || 'div',
1216             html : '',
1217             cls : ''
1218         };
1219         if (this.jumbotron) {
1220             cfg.cls = 'jumbotron';
1221         }
1222         
1223         
1224         
1225         // - this is applied by the parent..
1226         //if (this.cls) {
1227         //    cfg.cls = this.cls + '';
1228         //}
1229         
1230         if (this.sticky.length) {
1231             
1232             var bd = Roo.get(document.body);
1233             if (!bd.hasClass('bootstrap-sticky')) {
1234                 bd.addClass('bootstrap-sticky');
1235                 Roo.select('html',true).setStyle('height', '100%');
1236             }
1237              
1238             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1239         }
1240         
1241         
1242         if (this.well.length) {
1243             switch (this.well) {
1244                 case 'lg':
1245                 case 'sm':
1246                     cfg.cls +=' well well-' +this.well;
1247                     break;
1248                 default:
1249                     cfg.cls +=' well';
1250                     break;
1251             }
1252         }
1253         
1254         if (this.hidden) {
1255             cfg.cls += ' hidden';
1256         }
1257         
1258         
1259         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1260             cfg.cls +=' alert alert-' + this.alert;
1261         }
1262         
1263         var body = cfg;
1264         
1265         if (this.panel.length) {
1266             cfg.cls += ' panel panel-' + this.panel;
1267             cfg.cn = [];
1268             if (this.header.length) {
1269                 
1270                 var h = [];
1271                 
1272                 if(this.expandable){
1273                     
1274                     cfg.cls = cfg.cls + ' expandable';
1275                     
1276                     h.push({
1277                         tag: 'i',
1278                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1279                     });
1280                     
1281                 }
1282                 
1283                 h.push(
1284                     {
1285                         tag: 'span',
1286                         cls : 'panel-title',
1287                         html : (this.expandable ? '&nbsp;' : '') + this.header
1288                     },
1289                     {
1290                         tag: 'span',
1291                         cls: 'panel-header-right',
1292                         html: this.rheader
1293                     }
1294                 );
1295                 
1296                 cfg.cn.push({
1297                     cls : 'panel-heading',
1298                     style : this.expandable ? 'cursor: pointer' : '',
1299                     cn : h
1300                 });
1301                 
1302             }
1303             
1304             body = false;
1305             cfg.cn.push({
1306                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1307                 html : this.html
1308             });
1309             
1310             
1311             if (this.footer.length) {
1312                 cfg.cn.push({
1313                     cls : 'panel-footer',
1314                     html : this.footer
1315                     
1316                 });
1317             }
1318             
1319         }
1320         
1321         if (body) {
1322             body.html = this.html || cfg.html;
1323             // prefix with the icons..
1324             if (this.fa) {
1325                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1326             }
1327             if (this.icon) {
1328                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1329             }
1330             
1331             
1332         }
1333         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1334             cfg.cls =  'container';
1335         }
1336         
1337         return cfg;
1338     },
1339     
1340     initEvents: function() 
1341     {
1342         if(this.expandable){
1343             var headerEl = this.headerEl();
1344         
1345             if(headerEl){
1346                 headerEl.on('click', this.onToggleClick, this);
1347             }
1348         }
1349         
1350         if(this.clickable){
1351             this.el.on('click', this.onClick, this);
1352         }
1353         
1354     },
1355     
1356     onToggleClick : function()
1357     {
1358         var headerEl = this.headerEl();
1359         
1360         if(!headerEl){
1361             return;
1362         }
1363         
1364         if(this.expanded){
1365             this.collapse();
1366             return;
1367         }
1368         
1369         this.expand();
1370     },
1371     
1372     expand : function()
1373     {
1374         if(this.fireEvent('expand', this)) {
1375             
1376             this.expanded = true;
1377             
1378             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1379             
1380             this.el.select('.panel-body',true).first().removeClass('hide');
1381             
1382             var toggleEl = this.toggleEl();
1383
1384             if(!toggleEl){
1385                 return;
1386             }
1387
1388             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1389         }
1390         
1391     },
1392     
1393     collapse : function()
1394     {
1395         if(this.fireEvent('collapse', this)) {
1396             
1397             this.expanded = false;
1398             
1399             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1400             this.el.select('.panel-body',true).first().addClass('hide');
1401         
1402             var toggleEl = this.toggleEl();
1403
1404             if(!toggleEl){
1405                 return;
1406             }
1407
1408             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1409         }
1410     },
1411     
1412     toggleEl : function()
1413     {
1414         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1415             return;
1416         }
1417         
1418         return this.el.select('.panel-heading .fa',true).first();
1419     },
1420     
1421     headerEl : function()
1422     {
1423         if(!this.el || !this.panel.length || !this.header.length){
1424             return;
1425         }
1426         
1427         return this.el.select('.panel-heading',true).first()
1428     },
1429     
1430     bodyEl : function()
1431     {
1432         if(!this.el || !this.panel.length){
1433             return;
1434         }
1435         
1436         return this.el.select('.panel-body',true).first()
1437     },
1438     
1439     titleEl : function()
1440     {
1441         if(!this.el || !this.panel.length || !this.header.length){
1442             return;
1443         }
1444         
1445         return this.el.select('.panel-title',true).first();
1446     },
1447     
1448     setTitle : function(v)
1449     {
1450         var titleEl = this.titleEl();
1451         
1452         if(!titleEl){
1453             return;
1454         }
1455         
1456         titleEl.dom.innerHTML = v;
1457     },
1458     
1459     getTitle : function()
1460     {
1461         
1462         var titleEl = this.titleEl();
1463         
1464         if(!titleEl){
1465             return '';
1466         }
1467         
1468         return titleEl.dom.innerHTML;
1469     },
1470     
1471     setRightTitle : function(v)
1472     {
1473         var t = this.el.select('.panel-header-right',true).first();
1474         
1475         if(!t){
1476             return;
1477         }
1478         
1479         t.dom.innerHTML = v;
1480     },
1481     
1482     onClick : function(e)
1483     {
1484         e.preventDefault();
1485         
1486         this.fireEvent('click', this, e);
1487     }
1488 });
1489
1490  /*
1491  * - LGPL
1492  *
1493  * image
1494  * 
1495  */
1496
1497
1498 /**
1499  * @class Roo.bootstrap.Img
1500  * @extends Roo.bootstrap.Component
1501  * Bootstrap Img class
1502  * @cfg {Boolean} imgResponsive false | true
1503  * @cfg {String} border rounded | circle | thumbnail
1504  * @cfg {String} src image source
1505  * @cfg {String} alt image alternative text
1506  * @cfg {String} href a tag href
1507  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1508  * @cfg {String} xsUrl xs image source
1509  * @cfg {String} smUrl sm image source
1510  * @cfg {String} mdUrl md image source
1511  * @cfg {String} lgUrl lg image source
1512  * 
1513  * @constructor
1514  * Create a new Input
1515  * @param {Object} config The config object
1516  */
1517
1518 Roo.bootstrap.Img = function(config){
1519     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1520     
1521     this.addEvents({
1522         // img events
1523         /**
1524          * @event click
1525          * The img click event for the img.
1526          * @param {Roo.EventObject} e
1527          */
1528         "click" : true
1529     });
1530 };
1531
1532 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1533     
1534     imgResponsive: true,
1535     border: '',
1536     src: 'about:blank',
1537     href: false,
1538     target: false,
1539     xsUrl: '',
1540     smUrl: '',
1541     mdUrl: '',
1542     lgUrl: '',
1543
1544     getAutoCreate : function()
1545     {   
1546         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1547             return this.createSingleImg();
1548         }
1549         
1550         var cfg = {
1551             tag: 'div',
1552             cls: 'roo-image-responsive-group',
1553             cn: []
1554         };
1555         var _this = this;
1556         
1557         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1558             
1559             if(!_this[size + 'Url']){
1560                 return;
1561             }
1562             
1563             var img = {
1564                 tag: 'img',
1565                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1566                 html: _this.html || cfg.html,
1567                 src: _this[size + 'Url']
1568             };
1569             
1570             img.cls += ' roo-image-responsive-' + size;
1571             
1572             var s = ['xs', 'sm', 'md', 'lg'];
1573             
1574             s.splice(s.indexOf(size), 1);
1575             
1576             Roo.each(s, function(ss){
1577                 img.cls += ' hidden-' + ss;
1578             });
1579             
1580             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1581                 cfg.cls += ' img-' + _this.border;
1582             }
1583             
1584             if(_this.alt){
1585                 cfg.alt = _this.alt;
1586             }
1587             
1588             if(_this.href){
1589                 var a = {
1590                     tag: 'a',
1591                     href: _this.href,
1592                     cn: [
1593                         img
1594                     ]
1595                 };
1596
1597                 if(this.target){
1598                     a.target = _this.target;
1599                 }
1600             }
1601             
1602             cfg.cn.push((_this.href) ? a : img);
1603             
1604         });
1605         
1606         return cfg;
1607     },
1608     
1609     createSingleImg : function()
1610     {
1611         var cfg = {
1612             tag: 'img',
1613             cls: (this.imgResponsive) ? 'img-responsive' : '',
1614             html : null,
1615             src : 'about:blank'  // just incase src get's set to undefined?!?
1616         };
1617         
1618         cfg.html = this.html || cfg.html;
1619         
1620         cfg.src = this.src || cfg.src;
1621         
1622         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1623             cfg.cls += ' img-' + this.border;
1624         }
1625         
1626         if(this.alt){
1627             cfg.alt = this.alt;
1628         }
1629         
1630         if(this.href){
1631             var a = {
1632                 tag: 'a',
1633                 href: this.href,
1634                 cn: [
1635                     cfg
1636                 ]
1637             };
1638             
1639             if(this.target){
1640                 a.target = this.target;
1641             }
1642             
1643         }
1644         
1645         return (this.href) ? a : cfg;
1646     },
1647     
1648     initEvents: function() 
1649     {
1650         if(!this.href){
1651             this.el.on('click', this.onClick, this);
1652         }
1653         
1654     },
1655     
1656     onClick : function(e)
1657     {
1658         Roo.log('img onclick');
1659         this.fireEvent('click', this, e);
1660     },
1661     /**
1662      * Sets the url of the image - used to update it
1663      * @param {String} url the url of the image
1664      */
1665     
1666     setSrc : function(url)
1667     {
1668         this.src =  url;
1669         
1670         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1671             this.el.dom.src =  url;
1672             return;
1673         }
1674         
1675         this.el.select('img', true).first().dom.src =  url;
1676     }
1677     
1678     
1679    
1680 });
1681
1682  /*
1683  * - LGPL
1684  *
1685  * image
1686  * 
1687  */
1688
1689
1690 /**
1691  * @class Roo.bootstrap.Link
1692  * @extends Roo.bootstrap.Component
1693  * Bootstrap Link Class
1694  * @cfg {String} alt image alternative text
1695  * @cfg {String} href a tag href
1696  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1697  * @cfg {String} html the content of the link.
1698  * @cfg {String} anchor name for the anchor link
1699  * @cfg {String} fa - favicon
1700
1701  * @cfg {Boolean} preventDefault (true | false) default false
1702
1703  * 
1704  * @constructor
1705  * Create a new Input
1706  * @param {Object} config The config object
1707  */
1708
1709 Roo.bootstrap.Link = function(config){
1710     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1711     
1712     this.addEvents({
1713         // img events
1714         /**
1715          * @event click
1716          * The img click event for the img.
1717          * @param {Roo.EventObject} e
1718          */
1719         "click" : true
1720     });
1721 };
1722
1723 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1724     
1725     href: false,
1726     target: false,
1727     preventDefault: false,
1728     anchor : false,
1729     alt : false,
1730     fa: false,
1731
1732
1733     getAutoCreate : function()
1734     {
1735         var html = this.html || '';
1736         
1737         if (this.fa !== false) {
1738             html = '<i class="fa fa-' + this.fa + '"></i>';
1739         }
1740         var cfg = {
1741             tag: 'a'
1742         };
1743         // anchor's do not require html/href...
1744         if (this.anchor === false) {
1745             cfg.html = html;
1746             cfg.href = this.href || '#';
1747         } else {
1748             cfg.name = this.anchor;
1749             if (this.html !== false || this.fa !== false) {
1750                 cfg.html = html;
1751             }
1752             if (this.href !== false) {
1753                 cfg.href = this.href;
1754             }
1755         }
1756         
1757         if(this.alt !== false){
1758             cfg.alt = this.alt;
1759         }
1760         
1761         
1762         if(this.target !== false) {
1763             cfg.target = this.target;
1764         }
1765         
1766         return cfg;
1767     },
1768     
1769     initEvents: function() {
1770         
1771         if(!this.href || this.preventDefault){
1772             this.el.on('click', this.onClick, this);
1773         }
1774     },
1775     
1776     onClick : function(e)
1777     {
1778         if(this.preventDefault){
1779             e.preventDefault();
1780         }
1781         //Roo.log('img onclick');
1782         this.fireEvent('click', this, e);
1783     }
1784    
1785 });
1786
1787  /*
1788  * - LGPL
1789  *
1790  * header
1791  * 
1792  */
1793
1794 /**
1795  * @class Roo.bootstrap.Header
1796  * @extends Roo.bootstrap.Component
1797  * Bootstrap Header class
1798  * @cfg {String} html content of header
1799  * @cfg {Number} level (1|2|3|4|5|6) default 1
1800  * 
1801  * @constructor
1802  * Create a new Header
1803  * @param {Object} config The config object
1804  */
1805
1806
1807 Roo.bootstrap.Header  = function(config){
1808     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1809 };
1810
1811 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1812     
1813     //href : false,
1814     html : false,
1815     level : 1,
1816     
1817     
1818     
1819     getAutoCreate : function(){
1820         
1821         
1822         
1823         var cfg = {
1824             tag: 'h' + (1 *this.level),
1825             html: this.html || ''
1826         } ;
1827         
1828         return cfg;
1829     }
1830    
1831 });
1832
1833  
1834
1835  /*
1836  * Based on:
1837  * Ext JS Library 1.1.1
1838  * Copyright(c) 2006-2007, Ext JS, LLC.
1839  *
1840  * Originally Released Under LGPL - original licence link has changed is not relivant.
1841  *
1842  * Fork - LGPL
1843  * <script type="text/javascript">
1844  */
1845  
1846 /**
1847  * @class Roo.bootstrap.MenuMgr
1848  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1849  * @singleton
1850  */
1851 Roo.bootstrap.MenuMgr = function(){
1852    var menus, active, groups = {}, attached = false, lastShow = new Date();
1853
1854    // private - called when first menu is created
1855    function init(){
1856        menus = {};
1857        active = new Roo.util.MixedCollection();
1858        Roo.get(document).addKeyListener(27, function(){
1859            if(active.length > 0){
1860                hideAll();
1861            }
1862        });
1863    }
1864
1865    // private
1866    function hideAll(){
1867        if(active && active.length > 0){
1868            var c = active.clone();
1869            c.each(function(m){
1870                m.hide();
1871            });
1872        }
1873    }
1874
1875    // private
1876    function onHide(m){
1877        active.remove(m);
1878        if(active.length < 1){
1879            Roo.get(document).un("mouseup", onMouseDown);
1880             
1881            attached = false;
1882        }
1883    }
1884
1885    // private
1886    function onShow(m){
1887        var last = active.last();
1888        lastShow = new Date();
1889        active.add(m);
1890        if(!attached){
1891           Roo.get(document).on("mouseup", onMouseDown);
1892            
1893            attached = true;
1894        }
1895        if(m.parentMenu){
1896           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1897           m.parentMenu.activeChild = m;
1898        }else if(last && last.isVisible()){
1899           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1900        }
1901    }
1902
1903    // private
1904    function onBeforeHide(m){
1905        if(m.activeChild){
1906            m.activeChild.hide();
1907        }
1908        if(m.autoHideTimer){
1909            clearTimeout(m.autoHideTimer);
1910            delete m.autoHideTimer;
1911        }
1912    }
1913
1914    // private
1915    function onBeforeShow(m){
1916        var pm = m.parentMenu;
1917        if(!pm && !m.allowOtherMenus){
1918            hideAll();
1919        }else if(pm && pm.activeChild && active != m){
1920            pm.activeChild.hide();
1921        }
1922    }
1923
1924    // private this should really trigger on mouseup..
1925    function onMouseDown(e){
1926         Roo.log("on Mouse Up");
1927         
1928         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1929             Roo.log("MenuManager hideAll");
1930             hideAll();
1931             e.stopEvent();
1932         }
1933         
1934         
1935    }
1936
1937    // private
1938    function onBeforeCheck(mi, state){
1939        if(state){
1940            var g = groups[mi.group];
1941            for(var i = 0, l = g.length; i < l; i++){
1942                if(g[i] != mi){
1943                    g[i].setChecked(false);
1944                }
1945            }
1946        }
1947    }
1948
1949    return {
1950
1951        /**
1952         * Hides all menus that are currently visible
1953         */
1954        hideAll : function(){
1955             hideAll();  
1956        },
1957
1958        // private
1959        register : function(menu){
1960            if(!menus){
1961                init();
1962            }
1963            menus[menu.id] = menu;
1964            menu.on("beforehide", onBeforeHide);
1965            menu.on("hide", onHide);
1966            menu.on("beforeshow", onBeforeShow);
1967            menu.on("show", onShow);
1968            var g = menu.group;
1969            if(g && menu.events["checkchange"]){
1970                if(!groups[g]){
1971                    groups[g] = [];
1972                }
1973                groups[g].push(menu);
1974                menu.on("checkchange", onCheck);
1975            }
1976        },
1977
1978         /**
1979          * Returns a {@link Roo.menu.Menu} object
1980          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1981          * be used to generate and return a new Menu instance.
1982          */
1983        get : function(menu){
1984            if(typeof menu == "string"){ // menu id
1985                return menus[menu];
1986            }else if(menu.events){  // menu instance
1987                return menu;
1988            }
1989            /*else if(typeof menu.length == 'number'){ // array of menu items?
1990                return new Roo.bootstrap.Menu({items:menu});
1991            }else{ // otherwise, must be a config
1992                return new Roo.bootstrap.Menu(menu);
1993            }
1994            */
1995            return false;
1996        },
1997
1998        // private
1999        unregister : function(menu){
2000            delete menus[menu.id];
2001            menu.un("beforehide", onBeforeHide);
2002            menu.un("hide", onHide);
2003            menu.un("beforeshow", onBeforeShow);
2004            menu.un("show", onShow);
2005            var g = menu.group;
2006            if(g && menu.events["checkchange"]){
2007                groups[g].remove(menu);
2008                menu.un("checkchange", onCheck);
2009            }
2010        },
2011
2012        // private
2013        registerCheckable : function(menuItem){
2014            var g = menuItem.group;
2015            if(g){
2016                if(!groups[g]){
2017                    groups[g] = [];
2018                }
2019                groups[g].push(menuItem);
2020                menuItem.on("beforecheckchange", onBeforeCheck);
2021            }
2022        },
2023
2024        // private
2025        unregisterCheckable : function(menuItem){
2026            var g = menuItem.group;
2027            if(g){
2028                groups[g].remove(menuItem);
2029                menuItem.un("beforecheckchange", onBeforeCheck);
2030            }
2031        }
2032    };
2033 }();/*
2034  * - LGPL
2035  *
2036  * menu
2037  * 
2038  */
2039
2040 /**
2041  * @class Roo.bootstrap.Menu
2042  * @extends Roo.bootstrap.Component
2043  * Bootstrap Menu class - container for MenuItems
2044  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2045  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2046  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2047  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2048  * 
2049  * @constructor
2050  * Create a new Menu
2051  * @param {Object} config The config object
2052  */
2053
2054
2055 Roo.bootstrap.Menu = function(config){
2056     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2057     if (this.registerMenu && this.type != 'treeview')  {
2058         Roo.bootstrap.MenuMgr.register(this);
2059     }
2060     
2061     
2062     this.addEvents({
2063         /**
2064          * @event beforeshow
2065          * Fires before this menu is displayed (return false to block)
2066          * @param {Roo.menu.Menu} this
2067          */
2068         beforeshow : true,
2069         /**
2070          * @event beforehide
2071          * Fires before this menu is hidden (return false to block)
2072          * @param {Roo.menu.Menu} this
2073          */
2074         beforehide : true,
2075         /**
2076          * @event show
2077          * Fires after this menu is displayed
2078          * @param {Roo.menu.Menu} this
2079          */
2080         show : true,
2081         /**
2082          * @event hide
2083          * Fires after this menu is hidden
2084          * @param {Roo.menu.Menu} this
2085          */
2086         hide : true,
2087         /**
2088          * @event click
2089          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2090          * @param {Roo.menu.Menu} this
2091          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2092          * @param {Roo.EventObject} e
2093          */
2094         click : true,
2095         /**
2096          * @event mouseover
2097          * Fires when the mouse is hovering over this menu
2098          * @param {Roo.menu.Menu} this
2099          * @param {Roo.EventObject} e
2100          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2101          */
2102         mouseover : true,
2103         /**
2104          * @event mouseout
2105          * Fires when the mouse exits this menu
2106          * @param {Roo.menu.Menu} this
2107          * @param {Roo.EventObject} e
2108          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2109          */
2110         mouseout : true,
2111         /**
2112          * @event itemclick
2113          * Fires when a menu item contained in this menu is clicked
2114          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2115          * @param {Roo.EventObject} e
2116          */
2117         itemclick: true
2118     });
2119     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2120 };
2121
2122 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2123     
2124    /// html : false,
2125     //align : '',
2126     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2127     type: false,
2128     /**
2129      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2130      */
2131     registerMenu : true,
2132     
2133     menuItems :false, // stores the menu items..
2134     
2135     hidden:true,
2136         
2137     parentMenu : false,
2138     
2139     stopEvent : true,
2140     
2141     isLink : false,
2142     
2143     getChildContainer : function() {
2144         return this.el;  
2145     },
2146     
2147     getAutoCreate : function(){
2148          
2149         //if (['right'].indexOf(this.align)!==-1) {
2150         //    cfg.cn[1].cls += ' pull-right'
2151         //}
2152         
2153         
2154         var cfg = {
2155             tag : 'ul',
2156             cls : 'dropdown-menu' ,
2157             style : 'z-index:1000'
2158             
2159         };
2160         
2161         if (this.type === 'submenu') {
2162             cfg.cls = 'submenu active';
2163         }
2164         if (this.type === 'treeview') {
2165             cfg.cls = 'treeview-menu';
2166         }
2167         
2168         return cfg;
2169     },
2170     initEvents : function() {
2171         
2172        // Roo.log("ADD event");
2173        // Roo.log(this.triggerEl.dom);
2174         
2175         this.triggerEl.on('click', this.onTriggerClick, this);
2176         
2177         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2178         
2179         
2180         if (this.triggerEl.hasClass('nav-item')) {
2181             // dropdown toggle on the 'a' in BS4?
2182             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2183         } else {
2184             this.triggerEl.addClass('dropdown-toggle');
2185         }
2186         if (Roo.isTouch) {
2187             this.el.on('touchstart'  , this.onTouch, this);
2188         }
2189         this.el.on('click' , this.onClick, this);
2190
2191         this.el.on("mouseover", this.onMouseOver, this);
2192         this.el.on("mouseout", this.onMouseOut, this);
2193         
2194     },
2195     
2196     findTargetItem : function(e)
2197     {
2198         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2199         if(!t){
2200             return false;
2201         }
2202         //Roo.log(t);         Roo.log(t.id);
2203         if(t && t.id){
2204             //Roo.log(this.menuitems);
2205             return this.menuitems.get(t.id);
2206             
2207             //return this.items.get(t.menuItemId);
2208         }
2209         
2210         return false;
2211     },
2212     
2213     onTouch : function(e) 
2214     {
2215         Roo.log("menu.onTouch");
2216         //e.stopEvent(); this make the user popdown broken
2217         this.onClick(e);
2218     },
2219     
2220     onClick : function(e)
2221     {
2222         Roo.log("menu.onClick");
2223         
2224         var t = this.findTargetItem(e);
2225         if(!t || t.isContainer){
2226             return;
2227         }
2228         Roo.log(e);
2229         /*
2230         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2231             if(t == this.activeItem && t.shouldDeactivate(e)){
2232                 this.activeItem.deactivate();
2233                 delete this.activeItem;
2234                 return;
2235             }
2236             if(t.canActivate){
2237                 this.setActiveItem(t, true);
2238             }
2239             return;
2240             
2241             
2242         }
2243         */
2244        
2245         Roo.log('pass click event');
2246         
2247         t.onClick(e);
2248         
2249         this.fireEvent("click", this, t, e);
2250         
2251         var _this = this;
2252         
2253         if(!t.href.length || t.href == '#'){
2254             (function() { _this.hide(); }).defer(100);
2255         }
2256         
2257     },
2258     
2259     onMouseOver : function(e){
2260         var t  = this.findTargetItem(e);
2261         //Roo.log(t);
2262         //if(t){
2263         //    if(t.canActivate && !t.disabled){
2264         //        this.setActiveItem(t, true);
2265         //    }
2266         //}
2267         
2268         this.fireEvent("mouseover", this, e, t);
2269     },
2270     isVisible : function(){
2271         return !this.hidden;
2272     },
2273     onMouseOut : function(e){
2274         var t  = this.findTargetItem(e);
2275         
2276         //if(t ){
2277         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2278         //        this.activeItem.deactivate();
2279         //        delete this.activeItem;
2280         //    }
2281         //}
2282         this.fireEvent("mouseout", this, e, t);
2283     },
2284     
2285     
2286     /**
2287      * Displays this menu relative to another element
2288      * @param {String/HTMLElement/Roo.Element} element The element to align to
2289      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2290      * the element (defaults to this.defaultAlign)
2291      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2292      */
2293     show : function(el, pos, parentMenu)
2294     {
2295         if (false === this.fireEvent("beforeshow", this)) {
2296             Roo.log("show canceled");
2297             return;
2298         }
2299         this.parentMenu = parentMenu;
2300         if(!this.el){
2301             this.render();
2302         }
2303         
2304         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2305     },
2306      /**
2307      * Displays this menu at a specific xy position
2308      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2309      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2310      */
2311     showAt : function(xy, parentMenu, /* private: */_e){
2312         this.parentMenu = parentMenu;
2313         if(!this.el){
2314             this.render();
2315         }
2316         if(_e !== false){
2317             this.fireEvent("beforeshow", this);
2318             //xy = this.el.adjustForConstraints(xy);
2319         }
2320         
2321         //this.el.show();
2322         this.hideMenuItems();
2323         this.hidden = false;
2324         this.triggerEl.addClass('open');
2325         this.el.addClass('show');
2326         
2327         // reassign x when hitting right
2328         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2329             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2330         }
2331         
2332         // reassign y when hitting bottom
2333         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2334             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2335         }
2336         
2337         // but the list may align on trigger left or trigger top... should it be a properity?
2338         
2339         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2340             this.el.setXY(xy);
2341         }
2342         
2343         this.focus();
2344         this.fireEvent("show", this);
2345     },
2346     
2347     focus : function(){
2348         return;
2349         if(!this.hidden){
2350             this.doFocus.defer(50, this);
2351         }
2352     },
2353
2354     doFocus : function(){
2355         if(!this.hidden){
2356             this.focusEl.focus();
2357         }
2358     },
2359
2360     /**
2361      * Hides this menu and optionally all parent menus
2362      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2363      */
2364     hide : function(deep)
2365     {
2366         if (false === this.fireEvent("beforehide", this)) {
2367             Roo.log("hide canceled");
2368             return;
2369         }
2370         this.hideMenuItems();
2371         if(this.el && this.isVisible()){
2372            
2373             if(this.activeItem){
2374                 this.activeItem.deactivate();
2375                 this.activeItem = null;
2376             }
2377             this.triggerEl.removeClass('open');;
2378             this.el.removeClass('show');
2379             this.hidden = true;
2380             this.fireEvent("hide", this);
2381         }
2382         if(deep === true && this.parentMenu){
2383             this.parentMenu.hide(true);
2384         }
2385     },
2386     
2387     onTriggerClick : function(e)
2388     {
2389         Roo.log('trigger click');
2390         
2391         var target = e.getTarget();
2392         
2393         Roo.log(target.nodeName.toLowerCase());
2394         
2395         if(target.nodeName.toLowerCase() === 'i'){
2396             e.preventDefault();
2397         }
2398         
2399     },
2400     
2401     onTriggerPress  : function(e)
2402     {
2403         Roo.log('trigger press');
2404         //Roo.log(e.getTarget());
2405        // Roo.log(this.triggerEl.dom);
2406        
2407         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2408         var pel = Roo.get(e.getTarget());
2409         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2410             Roo.log('is treeview or dropdown?');
2411             return;
2412         }
2413         
2414         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2415             return;
2416         }
2417         
2418         if (this.isVisible()) {
2419             Roo.log('hide');
2420             this.hide();
2421         } else {
2422             Roo.log('show');
2423             this.show(this.triggerEl, '?', false);
2424         }
2425         
2426         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2427             e.stopEvent();
2428         }
2429         
2430     },
2431        
2432     
2433     hideMenuItems : function()
2434     {
2435         Roo.log("hide Menu Items");
2436         if (!this.el) { 
2437             return;
2438         }
2439         
2440         this.el.select('.open',true).each(function(aa) {
2441             
2442             aa.removeClass('open');
2443          
2444         });
2445     },
2446     addxtypeChild : function (tree, cntr) {
2447         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2448           
2449         this.menuitems.add(comp);
2450         return comp;
2451
2452     },
2453     getEl : function()
2454     {
2455         Roo.log(this.el);
2456         return this.el;
2457     },
2458     
2459     clear : function()
2460     {
2461         this.getEl().dom.innerHTML = '';
2462         this.menuitems.clear();
2463     }
2464 });
2465
2466  
2467  /*
2468  * - LGPL
2469  *
2470  * menu item
2471  * 
2472  */
2473
2474
2475 /**
2476  * @class Roo.bootstrap.MenuItem
2477  * @extends Roo.bootstrap.Component
2478  * Bootstrap MenuItem class
2479  * @cfg {String} html the menu label
2480  * @cfg {String} href the link
2481  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2482  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2483  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2484  * @cfg {String} fa favicon to show on left of menu item.
2485  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2486  * 
2487  * 
2488  * @constructor
2489  * Create a new MenuItem
2490  * @param {Object} config The config object
2491  */
2492
2493
2494 Roo.bootstrap.MenuItem = function(config){
2495     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2496     this.addEvents({
2497         // raw events
2498         /**
2499          * @event click
2500          * The raw click event for the entire grid.
2501          * @param {Roo.bootstrap.MenuItem} this
2502          * @param {Roo.EventObject} e
2503          */
2504         "click" : true
2505     });
2506 };
2507
2508 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2509     
2510     href : false,
2511     html : false,
2512     preventDefault: false,
2513     isContainer : false,
2514     active : false,
2515     fa: false,
2516     
2517     getAutoCreate : function(){
2518         
2519         if(this.isContainer){
2520             return {
2521                 tag: 'li',
2522                 cls: 'dropdown-menu-item '
2523             };
2524         }
2525         var ctag = {
2526             tag: 'span',
2527             html: 'Link'
2528         };
2529         
2530         var anc = {
2531             tag : 'a',
2532             cls : 'dropdown-item',
2533             href : '#',
2534             cn : [  ]
2535         };
2536         
2537         if (this.fa !== false) {
2538             anc.cn.push({
2539                 tag : 'i',
2540                 cls : 'fa fa-' + this.fa
2541             });
2542         }
2543         
2544         anc.cn.push(ctag);
2545         
2546         
2547         var cfg= {
2548             tag: 'li',
2549             cls: 'dropdown-menu-item',
2550             cn: [ anc ]
2551         };
2552         if (this.parent().type == 'treeview') {
2553             cfg.cls = 'treeview-menu';
2554         }
2555         if (this.active) {
2556             cfg.cls += ' active';
2557         }
2558         
2559         
2560         
2561         anc.href = this.href || cfg.cn[0].href ;
2562         ctag.html = this.html || cfg.cn[0].html ;
2563         return cfg;
2564     },
2565     
2566     initEvents: function()
2567     {
2568         if (this.parent().type == 'treeview') {
2569             this.el.select('a').on('click', this.onClick, this);
2570         }
2571         
2572         if (this.menu) {
2573             this.menu.parentType = this.xtype;
2574             this.menu.triggerEl = this.el;
2575             this.menu = this.addxtype(Roo.apply({}, this.menu));
2576         }
2577         
2578     },
2579     onClick : function(e)
2580     {
2581         Roo.log('item on click ');
2582         
2583         if(this.preventDefault){
2584             e.preventDefault();
2585         }
2586         //this.parent().hideMenuItems();
2587         
2588         this.fireEvent('click', this, e);
2589     },
2590     getEl : function()
2591     {
2592         return this.el;
2593     } 
2594 });
2595
2596  
2597
2598  /*
2599  * - LGPL
2600  *
2601  * menu separator
2602  * 
2603  */
2604
2605
2606 /**
2607  * @class Roo.bootstrap.MenuSeparator
2608  * @extends Roo.bootstrap.Component
2609  * Bootstrap MenuSeparator class
2610  * 
2611  * @constructor
2612  * Create a new MenuItem
2613  * @param {Object} config The config object
2614  */
2615
2616
2617 Roo.bootstrap.MenuSeparator = function(config){
2618     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2619 };
2620
2621 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2622     
2623     getAutoCreate : function(){
2624         var cfg = {
2625             cls: 'divider',
2626             tag : 'li'
2627         };
2628         
2629         return cfg;
2630     }
2631    
2632 });
2633
2634  
2635
2636  
2637 /*
2638 * Licence: LGPL
2639 */
2640
2641 /**
2642  * @class Roo.bootstrap.Modal
2643  * @extends Roo.bootstrap.Component
2644  * Bootstrap Modal class
2645  * @cfg {String} title Title of dialog
2646  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2647  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2648  * @cfg {Boolean} specificTitle default false
2649  * @cfg {Array} buttons Array of buttons or standard button set..
2650  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2651  * @cfg {Boolean} animate default true
2652  * @cfg {Boolean} allow_close default true
2653  * @cfg {Boolean} fitwindow default false
2654  * @cfg {Number} width fixed width - usefull for chrome extension only really.
2655  * @cfg {Number} height fixed height - usefull for chrome extension only really.
2656  * @cfg {String} size (sm|lg) default empty
2657  * @cfg {Number} max_width set the max width of modal
2658  *
2659  *
2660  * @constructor
2661  * Create a new Modal Dialog
2662  * @param {Object} config The config object
2663  */
2664
2665 Roo.bootstrap.Modal = function(config){
2666     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2667     this.addEvents({
2668         // raw events
2669         /**
2670          * @event btnclick
2671          * The raw btnclick event for the button
2672          * @param {Roo.EventObject} e
2673          */
2674         "btnclick" : true,
2675         /**
2676          * @event resize
2677          * Fire when dialog resize
2678          * @param {Roo.bootstrap.Modal} this
2679          * @param {Roo.EventObject} e
2680          */
2681         "resize" : true
2682     });
2683     this.buttons = this.buttons || [];
2684
2685     if (this.tmpl) {
2686         this.tmpl = Roo.factory(this.tmpl);
2687     }
2688
2689 };
2690
2691 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2692
2693     title : 'test dialog',
2694
2695     buttons : false,
2696
2697     // set on load...
2698
2699     html: false,
2700
2701     tmp: false,
2702
2703     specificTitle: false,
2704
2705     buttonPosition: 'right',
2706
2707     allow_close : true,
2708
2709     animate : true,
2710
2711     fitwindow: false,
2712     
2713      // private
2714     dialogEl: false,
2715     bodyEl:  false,
2716     footerEl:  false,
2717     titleEl:  false,
2718     closeEl:  false,
2719
2720     size: '',
2721     
2722     max_width: 0,
2723     
2724     max_height: 0,
2725     
2726     fit_content: false,
2727
2728     onRender : function(ct, position)
2729     {
2730         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2731
2732         if(!this.el){
2733             var cfg = Roo.apply({},  this.getAutoCreate());
2734             cfg.id = Roo.id();
2735             //if(!cfg.name){
2736             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2737             //}
2738             //if (!cfg.name.length) {
2739             //    delete cfg.name;
2740            // }
2741             if (this.cls) {
2742                 cfg.cls += ' ' + this.cls;
2743             }
2744             if (this.style) {
2745                 cfg.style = this.style;
2746             }
2747             this.el = Roo.get(document.body).createChild(cfg, position);
2748         }
2749         //var type = this.el.dom.type;
2750
2751
2752         if(this.tabIndex !== undefined){
2753             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2754         }
2755
2756         this.dialogEl = this.el.select('.modal-dialog',true).first();
2757         this.bodyEl = this.el.select('.modal-body',true).first();
2758         this.closeEl = this.el.select('.modal-header .close', true).first();
2759         this.headerEl = this.el.select('.modal-header',true).first();
2760         this.titleEl = this.el.select('.modal-title',true).first();
2761         this.footerEl = this.el.select('.modal-footer',true).first();
2762
2763         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2764         
2765         //this.el.addClass("x-dlg-modal");
2766
2767         if (this.buttons.length) {
2768             Roo.each(this.buttons, function(bb) {
2769                 var b = Roo.apply({}, bb);
2770                 b.xns = b.xns || Roo.bootstrap;
2771                 b.xtype = b.xtype || 'Button';
2772                 if (typeof(b.listeners) == 'undefined') {
2773                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2774                 }
2775
2776                 var btn = Roo.factory(b);
2777
2778                 btn.render(this.getButtonContainer());
2779
2780             },this);
2781         }
2782         // render the children.
2783         var nitems = [];
2784
2785         if(typeof(this.items) != 'undefined'){
2786             var items = this.items;
2787             delete this.items;
2788
2789             for(var i =0;i < items.length;i++) {
2790                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2791             }
2792         }
2793
2794         this.items = nitems;
2795
2796         // where are these used - they used to be body/close/footer
2797
2798
2799         this.initEvents();
2800         //this.el.addClass([this.fieldClass, this.cls]);
2801
2802     },
2803
2804     getAutoCreate : function()
2805     {
2806         // we will default to modal-body-overflow - might need to remove or make optional later.
2807         var bdy = {
2808                 cls : 'modal-body enable-modal-body-overflow ', 
2809                 html : this.html || ''
2810         };
2811
2812         var title = {
2813             tag: 'h4',
2814             cls : 'modal-title',
2815             html : this.title
2816         };
2817
2818         if(this.specificTitle){
2819             title = this.title;
2820
2821         }
2822
2823         var header = [];
2824         if (this.allow_close && Roo.bootstrap.version == 3) {
2825             header.push({
2826                 tag: 'button',
2827                 cls : 'close',
2828                 html : '&times'
2829             });
2830         }
2831
2832         header.push(title);
2833
2834         if (this.allow_close && Roo.bootstrap.version == 4) {
2835             header.push({
2836                 tag: 'button',
2837                 cls : 'close',
2838                 html : '&times'
2839             });
2840         }
2841         
2842         var size = '';
2843
2844         if(this.size.length){
2845             size = 'modal-' + this.size;
2846         }
2847         
2848         var footer = Roo.bootstrap.version == 3 ?
2849             {
2850                 cls : 'modal-footer',
2851                 cn : [
2852                     {
2853                         tag: 'div',
2854                         cls: 'btn-' + this.buttonPosition
2855                     }
2856                 ]
2857
2858             } :
2859             {  // BS4 uses mr-auto on left buttons....
2860                 cls : 'modal-footer'
2861             };
2862
2863             
2864
2865         
2866         
2867         var modal = {
2868             cls: "modal",
2869              cn : [
2870                 {
2871                     cls: "modal-dialog " + size,
2872                     cn : [
2873                         {
2874                             cls : "modal-content",
2875                             cn : [
2876                                 {
2877                                     cls : 'modal-header',
2878                                     cn : header
2879                                 },
2880                                 bdy,
2881                                 footer
2882                             ]
2883
2884                         }
2885                     ]
2886
2887                 }
2888             ]
2889         };
2890
2891         if(this.animate){
2892             modal.cls += ' fade';
2893         }
2894
2895         return modal;
2896
2897     },
2898     getChildContainer : function() {
2899
2900          return this.bodyEl;
2901
2902     },
2903     getButtonContainer : function() {
2904         
2905          return Roo.bootstrap.version == 4 ?
2906             this.el.select('.modal-footer',true).first()
2907             : this.el.select('.modal-footer div',true).first();
2908
2909     },
2910     initEvents : function()
2911     {
2912         if (this.allow_close) {
2913             this.closeEl.on('click', this.hide, this);
2914         }
2915         Roo.EventManager.onWindowResize(this.resize, this, true);
2916
2917
2918     },
2919   
2920
2921     resize : function()
2922     {
2923         this.maskEl.setSize(
2924             Roo.lib.Dom.getViewWidth(true),
2925             Roo.lib.Dom.getViewHeight(true)
2926         );
2927         
2928         if (this.fitwindow) {
2929             
2930            
2931             this.setSize(
2932                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2933                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
2934             );
2935             return;
2936         }
2937         
2938         if(this.max_width !== 0) {
2939             
2940             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2941             
2942             if(this.height) {
2943                 this.setSize(w, this.height);
2944                 return;
2945             }
2946             
2947             if(this.max_height) {
2948                 this.setSize(w,Math.min(
2949                     this.max_height,
2950                     Roo.lib.Dom.getViewportHeight(true) - 60
2951                 ));
2952                 
2953                 return;
2954             }
2955             
2956             if(!this.fit_content) {
2957                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2958                 return;
2959             }
2960             
2961             this.setSize(w, Math.min(
2962                 60 +
2963                 this.headerEl.getHeight() + 
2964                 this.footerEl.getHeight() + 
2965                 this.getChildHeight(this.bodyEl.dom.childNodes),
2966                 Roo.lib.Dom.getViewportHeight(true) - 60)
2967             );
2968         }
2969         
2970     },
2971
2972     setSize : function(w,h)
2973     {
2974         if (!w && !h) {
2975             return;
2976         }
2977         
2978         this.resizeTo(w,h);
2979     },
2980
2981     show : function() {
2982
2983         if (!this.rendered) {
2984             this.render();
2985         }
2986
2987         //this.el.setStyle('display', 'block');
2988         this.el.removeClass('hideing');
2989         this.el.dom.style.display='block';
2990         
2991         Roo.get(document.body).addClass('modal-open');
2992  
2993         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2994             
2995             (function(){
2996                 this.el.addClass('show');
2997                 this.el.addClass('in');
2998             }).defer(50, this);
2999         }else{
3000             this.el.addClass('show');
3001             this.el.addClass('in');
3002         }
3003
3004         // not sure how we can show data in here..
3005         //if (this.tmpl) {
3006         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3007         //}
3008
3009         Roo.get(document.body).addClass("x-body-masked");
3010         
3011         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3012         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3013         this.maskEl.dom.style.display = 'block';
3014         this.maskEl.addClass('show');
3015         
3016         
3017         this.resize();
3018         
3019         this.fireEvent('show', this);
3020
3021         // set zindex here - otherwise it appears to be ignored...
3022         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3023
3024         (function () {
3025             this.items.forEach( function(e) {
3026                 e.layout ? e.layout() : false;
3027
3028             });
3029         }).defer(100,this);
3030
3031     },
3032     hide : function()
3033     {
3034         if(this.fireEvent("beforehide", this) !== false){
3035             
3036             this.maskEl.removeClass('show');
3037             
3038             this.maskEl.dom.style.display = '';
3039             Roo.get(document.body).removeClass("x-body-masked");
3040             this.el.removeClass('in');
3041             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3042
3043             if(this.animate){ // why
3044                 this.el.addClass('hideing');
3045                 this.el.removeClass('show');
3046                 (function(){
3047                     if (!this.el.hasClass('hideing')) {
3048                         return; // it's been shown again...
3049                     }
3050                     
3051                     this.el.dom.style.display='';
3052
3053                     Roo.get(document.body).removeClass('modal-open');
3054                     this.el.removeClass('hideing');
3055                 }).defer(150,this);
3056                 
3057             }else{
3058                 this.el.removeClass('show');
3059                 this.el.dom.style.display='';
3060                 Roo.get(document.body).removeClass('modal-open');
3061
3062             }
3063             this.fireEvent('hide', this);
3064         }
3065     },
3066     isVisible : function()
3067     {
3068         
3069         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3070         
3071     },
3072
3073     addButton : function(str, cb)
3074     {
3075
3076
3077         var b = Roo.apply({}, { html : str } );
3078         b.xns = b.xns || Roo.bootstrap;
3079         b.xtype = b.xtype || 'Button';
3080         if (typeof(b.listeners) == 'undefined') {
3081             b.listeners = { click : cb.createDelegate(this)  };
3082         }
3083
3084         var btn = Roo.factory(b);
3085
3086         btn.render(this.getButtonContainer());
3087
3088         return btn;
3089
3090     },
3091
3092     setDefaultButton : function(btn)
3093     {
3094         //this.el.select('.modal-footer').()
3095     },
3096
3097     resizeTo: function(w,h)
3098     {
3099         this.dialogEl.setWidth(w);
3100         
3101         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
3102
3103         this.bodyEl.setHeight(h - diff);
3104         
3105         this.fireEvent('resize', this);
3106     },
3107     
3108     setContentSize  : function(w, h)
3109     {
3110
3111     },
3112     onButtonClick: function(btn,e)
3113     {
3114         //Roo.log([a,b,c]);
3115         this.fireEvent('btnclick', btn.name, e);
3116     },
3117      /**
3118      * Set the title of the Dialog
3119      * @param {String} str new Title
3120      */
3121     setTitle: function(str) {
3122         this.titleEl.dom.innerHTML = str;
3123     },
3124     /**
3125      * Set the body of the Dialog
3126      * @param {String} str new Title
3127      */
3128     setBody: function(str) {
3129         this.bodyEl.dom.innerHTML = str;
3130     },
3131     /**
3132      * Set the body of the Dialog using the template
3133      * @param {Obj} data - apply this data to the template and replace the body contents.
3134      */
3135     applyBody: function(obj)
3136     {
3137         if (!this.tmpl) {
3138             Roo.log("Error - using apply Body without a template");
3139             //code
3140         }
3141         this.tmpl.overwrite(this.bodyEl, obj);
3142     },
3143     
3144     getChildHeight : function(child_nodes)
3145     {
3146         if(
3147             !child_nodes ||
3148             child_nodes.length == 0
3149         ) {
3150             return;
3151         }
3152         
3153         var child_height = 0;
3154         
3155         for(var i = 0; i < child_nodes.length; i++) {
3156             
3157             /*
3158             * for modal with tabs...
3159             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3160                 
3161                 var layout_childs = child_nodes[i].childNodes;
3162                 
3163                 for(var j = 0; j < layout_childs.length; j++) {
3164                     
3165                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3166                         
3167                         var layout_body_childs = layout_childs[j].childNodes;
3168                         
3169                         for(var k = 0; k < layout_body_childs.length; k++) {
3170                             
3171                             if(layout_body_childs[k].classList.contains('navbar')) {
3172                                 child_height += layout_body_childs[k].offsetHeight;
3173                                 continue;
3174                             }
3175                             
3176                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3177                                 
3178                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3179                                 
3180                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3181                                     
3182                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3183                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3184                                         continue;
3185                                     }
3186                                     
3187                                 }
3188                                 
3189                             }
3190                             
3191                         }
3192                     }
3193                 }
3194                 continue;
3195             }
3196             */
3197             
3198             child_height += child_nodes[i].offsetHeight;
3199             // Roo.log(child_nodes[i].offsetHeight);
3200         }
3201         
3202         return child_height;
3203     }
3204
3205 });
3206
3207
3208 Roo.apply(Roo.bootstrap.Modal,  {
3209     /**
3210          * Button config that displays a single OK button
3211          * @type Object
3212          */
3213         OK :  [{
3214             name : 'ok',
3215             weight : 'primary',
3216             html : 'OK'
3217         }],
3218         /**
3219          * Button config that displays Yes and No buttons
3220          * @type Object
3221          */
3222         YESNO : [
3223             {
3224                 name  : 'no',
3225                 html : 'No'
3226             },
3227             {
3228                 name  :'yes',
3229                 weight : 'primary',
3230                 html : 'Yes'
3231             }
3232         ],
3233
3234         /**
3235          * Button config that displays OK and Cancel buttons
3236          * @type Object
3237          */
3238         OKCANCEL : [
3239             {
3240                name : 'cancel',
3241                 html : 'Cancel'
3242             },
3243             {
3244                 name : 'ok',
3245                 weight : 'primary',
3246                 html : 'OK'
3247             }
3248         ],
3249         /**
3250          * Button config that displays Yes, No and Cancel buttons
3251          * @type Object
3252          */
3253         YESNOCANCEL : [
3254             {
3255                 name : 'yes',
3256                 weight : 'primary',
3257                 html : 'Yes'
3258             },
3259             {
3260                 name : 'no',
3261                 html : 'No'
3262             },
3263             {
3264                 name : 'cancel',
3265                 html : 'Cancel'
3266             }
3267         ],
3268         
3269         zIndex : 10001
3270 });
3271 /*
3272  * - LGPL
3273  *
3274  * messagebox - can be used as a replace
3275  * 
3276  */
3277 /**
3278  * @class Roo.MessageBox
3279  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3280  * Example usage:
3281  *<pre><code>
3282 // Basic alert:
3283 Roo.Msg.alert('Status', 'Changes saved successfully.');
3284
3285 // Prompt for user data:
3286 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3287     if (btn == 'ok'){
3288         // process text value...
3289     }
3290 });
3291
3292 // Show a dialog using config options:
3293 Roo.Msg.show({
3294    title:'Save Changes?',
3295    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3296    buttons: Roo.Msg.YESNOCANCEL,
3297    fn: processResult,
3298    animEl: 'elId'
3299 });
3300 </code></pre>
3301  * @singleton
3302  */
3303 Roo.bootstrap.MessageBox = function(){
3304     var dlg, opt, mask, waitTimer;
3305     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3306     var buttons, activeTextEl, bwidth;
3307
3308     
3309     // private
3310     var handleButton = function(button){
3311         dlg.hide();
3312         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3313     };
3314
3315     // private
3316     var handleHide = function(){
3317         if(opt && opt.cls){
3318             dlg.el.removeClass(opt.cls);
3319         }
3320         //if(waitTimer){
3321         //    Roo.TaskMgr.stop(waitTimer);
3322         //    waitTimer = null;
3323         //}
3324     };
3325
3326     // private
3327     var updateButtons = function(b){
3328         var width = 0;
3329         if(!b){
3330             buttons["ok"].hide();
3331             buttons["cancel"].hide();
3332             buttons["yes"].hide();
3333             buttons["no"].hide();
3334             dlg.footerEl.hide();
3335             
3336             return width;
3337         }
3338         dlg.footerEl.show();
3339         for(var k in buttons){
3340             if(typeof buttons[k] != "function"){
3341                 if(b[k]){
3342                     buttons[k].show();
3343                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3344                     width += buttons[k].el.getWidth()+15;
3345                 }else{
3346                     buttons[k].hide();
3347                 }
3348             }
3349         }
3350         return width;
3351     };
3352
3353     // private
3354     var handleEsc = function(d, k, e){
3355         if(opt && opt.closable !== false){
3356             dlg.hide();
3357         }
3358         if(e){
3359             e.stopEvent();
3360         }
3361     };
3362
3363     return {
3364         /**
3365          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3366          * @return {Roo.BasicDialog} The BasicDialog element
3367          */
3368         getDialog : function(){
3369            if(!dlg){
3370                 dlg = new Roo.bootstrap.Modal( {
3371                     //draggable: true,
3372                     //resizable:false,
3373                     //constraintoviewport:false,
3374                     //fixedcenter:true,
3375                     //collapsible : false,
3376                     //shim:true,
3377                     //modal: true,
3378                 //    width: 'auto',
3379                   //  height:100,
3380                     //buttonAlign:"center",
3381                     closeClick : function(){
3382                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3383                             handleButton("no");
3384                         }else{
3385                             handleButton("cancel");
3386                         }
3387                     }
3388                 });
3389                 dlg.render();
3390                 dlg.on("hide", handleHide);
3391                 mask = dlg.mask;
3392                 //dlg.addKeyListener(27, handleEsc);
3393                 buttons = {};
3394                 this.buttons = buttons;
3395                 var bt = this.buttonText;
3396                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3397                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3398                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3399                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3400                 //Roo.log(buttons);
3401                 bodyEl = dlg.bodyEl.createChild({
3402
3403                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3404                         '<textarea class="roo-mb-textarea"></textarea>' +
3405                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3406                 });
3407                 msgEl = bodyEl.dom.firstChild;
3408                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3409                 textboxEl.enableDisplayMode();
3410                 textboxEl.addKeyListener([10,13], function(){
3411                     if(dlg.isVisible() && opt && opt.buttons){
3412                         if(opt.buttons.ok){
3413                             handleButton("ok");
3414                         }else if(opt.buttons.yes){
3415                             handleButton("yes");
3416                         }
3417                     }
3418                 });
3419                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3420                 textareaEl.enableDisplayMode();
3421                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3422                 progressEl.enableDisplayMode();
3423                 
3424                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3425                 var pf = progressEl.dom.firstChild;
3426                 if (pf) {
3427                     pp = Roo.get(pf.firstChild);
3428                     pp.setHeight(pf.offsetHeight);
3429                 }
3430                 
3431             }
3432             return dlg;
3433         },
3434
3435         /**
3436          * Updates the message box body text
3437          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3438          * the XHTML-compliant non-breaking space character '&amp;#160;')
3439          * @return {Roo.MessageBox} This message box
3440          */
3441         updateText : function(text)
3442         {
3443             if(!dlg.isVisible() && !opt.width){
3444                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3445                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3446             }
3447             msgEl.innerHTML = text || '&#160;';
3448       
3449             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3450             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3451             var w = Math.max(
3452                     Math.min(opt.width || cw , this.maxWidth), 
3453                     Math.max(opt.minWidth || this.minWidth, bwidth)
3454             );
3455             if(opt.prompt){
3456                 activeTextEl.setWidth(w);
3457             }
3458             if(dlg.isVisible()){
3459                 dlg.fixedcenter = false;
3460             }
3461             // to big, make it scroll. = But as usual stupid IE does not support
3462             // !important..
3463             
3464             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3465                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3466                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3467             } else {
3468                 bodyEl.dom.style.height = '';
3469                 bodyEl.dom.style.overflowY = '';
3470             }
3471             if (cw > w) {
3472                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3473             } else {
3474                 bodyEl.dom.style.overflowX = '';
3475             }
3476             
3477             dlg.setContentSize(w, bodyEl.getHeight());
3478             if(dlg.isVisible()){
3479                 dlg.fixedcenter = true;
3480             }
3481             return this;
3482         },
3483
3484         /**
3485          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3486          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3487          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3488          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3489          * @return {Roo.MessageBox} This message box
3490          */
3491         updateProgress : function(value, text){
3492             if(text){
3493                 this.updateText(text);
3494             }
3495             
3496             if (pp) { // weird bug on my firefox - for some reason this is not defined
3497                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3498                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3499             }
3500             return this;
3501         },        
3502
3503         /**
3504          * Returns true if the message box is currently displayed
3505          * @return {Boolean} True if the message box is visible, else false
3506          */
3507         isVisible : function(){
3508             return dlg && dlg.isVisible();  
3509         },
3510
3511         /**
3512          * Hides the message box if it is displayed
3513          */
3514         hide : function(){
3515             if(this.isVisible()){
3516                 dlg.hide();
3517             }  
3518         },
3519
3520         /**
3521          * Displays a new message box, or reinitializes an existing message box, based on the config options
3522          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3523          * The following config object properties are supported:
3524          * <pre>
3525 Property    Type             Description
3526 ----------  ---------------  ------------------------------------------------------------------------------------
3527 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3528                                    closes (defaults to undefined)
3529 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3530                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3531 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3532                                    progress and wait dialogs will ignore this property and always hide the
3533                                    close button as they can only be closed programmatically.
3534 cls               String           A custom CSS class to apply to the message box element
3535 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3536                                    displayed (defaults to 75)
3537 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3538                                    function will be btn (the name of the button that was clicked, if applicable,
3539                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3540                                    Progress and wait dialogs will ignore this option since they do not respond to
3541                                    user actions and can only be closed programmatically, so any required function
3542                                    should be called by the same code after it closes the dialog.
3543 icon              String           A CSS class that provides a background image to be used as an icon for
3544                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3545 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3546 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3547 modal             Boolean          False to allow user interaction with the page while the message box is
3548                                    displayed (defaults to true)
3549 msg               String           A string that will replace the existing message box body text (defaults
3550                                    to the XHTML-compliant non-breaking space character '&#160;')
3551 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3552 progress          Boolean          True to display a progress bar (defaults to false)
3553 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3554 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3555 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3556 title             String           The title text
3557 value             String           The string value to set into the active textbox element if displayed
3558 wait              Boolean          True to display a progress bar (defaults to false)
3559 width             Number           The width of the dialog in pixels
3560 </pre>
3561          *
3562          * Example usage:
3563          * <pre><code>
3564 Roo.Msg.show({
3565    title: 'Address',
3566    msg: 'Please enter your address:',
3567    width: 300,
3568    buttons: Roo.MessageBox.OKCANCEL,
3569    multiline: true,
3570    fn: saveAddress,
3571    animEl: 'addAddressBtn'
3572 });
3573 </code></pre>
3574          * @param {Object} config Configuration options
3575          * @return {Roo.MessageBox} This message box
3576          */
3577         show : function(options)
3578         {
3579             
3580             // this causes nightmares if you show one dialog after another
3581             // especially on callbacks..
3582              
3583             if(this.isVisible()){
3584                 
3585                 this.hide();
3586                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3587                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3588                 Roo.log("New Dialog Message:" +  options.msg )
3589                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3590                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3591                 
3592             }
3593             var d = this.getDialog();
3594             opt = options;
3595             d.setTitle(opt.title || "&#160;");
3596             d.closeEl.setDisplayed(opt.closable !== false);
3597             activeTextEl = textboxEl;
3598             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3599             if(opt.prompt){
3600                 if(opt.multiline){
3601                     textboxEl.hide();
3602                     textareaEl.show();
3603                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3604                         opt.multiline : this.defaultTextHeight);
3605                     activeTextEl = textareaEl;
3606                 }else{
3607                     textboxEl.show();
3608                     textareaEl.hide();
3609                 }
3610             }else{
3611                 textboxEl.hide();
3612                 textareaEl.hide();
3613             }
3614             progressEl.setDisplayed(opt.progress === true);
3615             if (opt.progress) {
3616                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3617             }
3618             this.updateProgress(0);
3619             activeTextEl.dom.value = opt.value || "";
3620             if(opt.prompt){
3621                 dlg.setDefaultButton(activeTextEl);
3622             }else{
3623                 var bs = opt.buttons;
3624                 var db = null;
3625                 if(bs && bs.ok){
3626                     db = buttons["ok"];
3627                 }else if(bs && bs.yes){
3628                     db = buttons["yes"];
3629                 }
3630                 dlg.setDefaultButton(db);
3631             }
3632             bwidth = updateButtons(opt.buttons);
3633             this.updateText(opt.msg);
3634             if(opt.cls){
3635                 d.el.addClass(opt.cls);
3636             }
3637             d.proxyDrag = opt.proxyDrag === true;
3638             d.modal = opt.modal !== false;
3639             d.mask = opt.modal !== false ? mask : false;
3640             if(!d.isVisible()){
3641                 // force it to the end of the z-index stack so it gets a cursor in FF
3642                 document.body.appendChild(dlg.el.dom);
3643                 d.animateTarget = null;
3644                 d.show(options.animEl);
3645             }
3646             return this;
3647         },
3648
3649         /**
3650          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3651          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3652          * and closing the message box when the process is complete.
3653          * @param {String} title The title bar text
3654          * @param {String} msg The message box body text
3655          * @return {Roo.MessageBox} This message box
3656          */
3657         progress : function(title, msg){
3658             this.show({
3659                 title : title,
3660                 msg : msg,
3661                 buttons: false,
3662                 progress:true,
3663                 closable:false,
3664                 minWidth: this.minProgressWidth,
3665                 modal : true
3666             });
3667             return this;
3668         },
3669
3670         /**
3671          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3672          * If a callback function is passed it will be called after the user clicks the button, and the
3673          * id of the button that was clicked will be passed as the only parameter to the callback
3674          * (could also be the top-right close button).
3675          * @param {String} title The title bar text
3676          * @param {String} msg The message box body text
3677          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3678          * @param {Object} scope (optional) The scope of the callback function
3679          * @return {Roo.MessageBox} This message box
3680          */
3681         alert : function(title, msg, fn, scope)
3682         {
3683             this.show({
3684                 title : title,
3685                 msg : msg,
3686                 buttons: this.OK,
3687                 fn: fn,
3688                 closable : false,
3689                 scope : scope,
3690                 modal : true
3691             });
3692             return this;
3693         },
3694
3695         /**
3696          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3697          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3698          * You are responsible for closing the message box when the process is complete.
3699          * @param {String} msg The message box body text
3700          * @param {String} title (optional) The title bar text
3701          * @return {Roo.MessageBox} This message box
3702          */
3703         wait : function(msg, title){
3704             this.show({
3705                 title : title,
3706                 msg : msg,
3707                 buttons: false,
3708                 closable:false,
3709                 progress:true,
3710                 modal:true,
3711                 width:300,
3712                 wait:true
3713             });
3714             waitTimer = Roo.TaskMgr.start({
3715                 run: function(i){
3716                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3717                 },
3718                 interval: 1000
3719             });
3720             return this;
3721         },
3722
3723         /**
3724          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3725          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3726          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3727          * @param {String} title The title bar text
3728          * @param {String} msg The message box body text
3729          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3730          * @param {Object} scope (optional) The scope of the callback function
3731          * @return {Roo.MessageBox} This message box
3732          */
3733         confirm : function(title, msg, fn, scope){
3734             this.show({
3735                 title : title,
3736                 msg : msg,
3737                 buttons: this.YESNO,
3738                 fn: fn,
3739                 scope : scope,
3740                 modal : true
3741             });
3742             return this;
3743         },
3744
3745         /**
3746          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3747          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3748          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3749          * (could also be the top-right close button) and the text that was entered will be passed as the two
3750          * parameters to the callback.
3751          * @param {String} title The title bar text
3752          * @param {String} msg The message box body text
3753          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3754          * @param {Object} scope (optional) The scope of the callback function
3755          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3756          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3757          * @return {Roo.MessageBox} This message box
3758          */
3759         prompt : function(title, msg, fn, scope, multiline){
3760             this.show({
3761                 title : title,
3762                 msg : msg,
3763                 buttons: this.OKCANCEL,
3764                 fn: fn,
3765                 minWidth:250,
3766                 scope : scope,
3767                 prompt:true,
3768                 multiline: multiline,
3769                 modal : true
3770             });
3771             return this;
3772         },
3773
3774         /**
3775          * Button config that displays a single OK button
3776          * @type Object
3777          */
3778         OK : {ok:true},
3779         /**
3780          * Button config that displays Yes and No buttons
3781          * @type Object
3782          */
3783         YESNO : {yes:true, no:true},
3784         /**
3785          * Button config that displays OK and Cancel buttons
3786          * @type Object
3787          */
3788         OKCANCEL : {ok:true, cancel:true},
3789         /**
3790          * Button config that displays Yes, No and Cancel buttons
3791          * @type Object
3792          */
3793         YESNOCANCEL : {yes:true, no:true, cancel:true},
3794
3795         /**
3796          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3797          * @type Number
3798          */
3799         defaultTextHeight : 75,
3800         /**
3801          * The maximum width in pixels of the message box (defaults to 600)
3802          * @type Number
3803          */
3804         maxWidth : 600,
3805         /**
3806          * The minimum width in pixels of the message box (defaults to 100)
3807          * @type Number
3808          */
3809         minWidth : 100,
3810         /**
3811          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3812          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3813          * @type Number
3814          */
3815         minProgressWidth : 250,
3816         /**
3817          * An object containing the default button text strings that can be overriden for localized language support.
3818          * Supported properties are: ok, cancel, yes and no.
3819          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3820          * @type Object
3821          */
3822         buttonText : {
3823             ok : "OK",
3824             cancel : "Cancel",
3825             yes : "Yes",
3826             no : "No"
3827         }
3828     };
3829 }();
3830
3831 /**
3832  * Shorthand for {@link Roo.MessageBox}
3833  */
3834 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3835 Roo.Msg = Roo.Msg || Roo.MessageBox;
3836 /*
3837  * - LGPL
3838  *
3839  * navbar
3840  * 
3841  */
3842
3843 /**
3844  * @class Roo.bootstrap.Navbar
3845  * @extends Roo.bootstrap.Component
3846  * Bootstrap Navbar class
3847
3848  * @constructor
3849  * Create a new Navbar
3850  * @param {Object} config The config object
3851  */
3852
3853
3854 Roo.bootstrap.Navbar = function(config){
3855     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3856     this.addEvents({
3857         // raw events
3858         /**
3859          * @event beforetoggle
3860          * Fire before toggle the menu
3861          * @param {Roo.EventObject} e
3862          */
3863         "beforetoggle" : true
3864     });
3865 };
3866
3867 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3868     
3869     
3870    
3871     // private
3872     navItems : false,
3873     loadMask : false,
3874     
3875     
3876     getAutoCreate : function(){
3877         
3878         
3879         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3880         
3881     },
3882     
3883     initEvents :function ()
3884     {
3885         //Roo.log(this.el.select('.navbar-toggle',true));
3886         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
3887         
3888         var mark = {
3889             tag: "div",
3890             cls:"x-dlg-mask"
3891         };
3892         
3893         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3894         
3895         var size = this.el.getSize();
3896         this.maskEl.setSize(size.width, size.height);
3897         this.maskEl.enableDisplayMode("block");
3898         this.maskEl.hide();
3899         
3900         if(this.loadMask){
3901             this.maskEl.show();
3902         }
3903     },
3904     
3905     
3906     getChildContainer : function()
3907     {
3908         if (this.el && this.el.select('.collapse').getCount()) {
3909             return this.el.select('.collapse',true).first();
3910         }
3911         
3912         return this.el;
3913     },
3914     
3915     mask : function()
3916     {
3917         this.maskEl.show();
3918     },
3919     
3920     unmask : function()
3921     {
3922         this.maskEl.hide();
3923     },
3924     onToggle : function()
3925     {
3926         
3927         if(this.fireEvent('beforetoggle', this) === false){
3928             return;
3929         }
3930         var ce = this.el.select('.navbar-collapse',true).first();
3931       
3932         if (!ce.hasClass('show')) {
3933            this.expand();
3934         } else {
3935             this.collapse();
3936         }
3937         
3938         
3939     
3940     },
3941     /**
3942      * Expand the navbar pulldown 
3943      */
3944     expand : function ()
3945     {
3946        
3947         var ce = this.el.select('.navbar-collapse',true).first();
3948         if (ce.hasClass('collapsing')) {
3949             return;
3950         }
3951         ce.dom.style.height = '';
3952                // show it...
3953         ce.addClass('in'); // old...
3954         ce.removeClass('collapse');
3955         ce.addClass('show');
3956         var h = ce.getHeight();
3957         Roo.log(h);
3958         ce.removeClass('show');
3959         // at this point we should be able to see it..
3960         ce.addClass('collapsing');
3961         
3962         ce.setHeight(0); // resize it ...
3963         ce.on('transitionend', function() {
3964             //Roo.log('done transition');
3965             ce.removeClass('collapsing');
3966             ce.addClass('show');
3967             ce.removeClass('collapse');
3968
3969             ce.dom.style.height = '';
3970         }, this, { single: true} );
3971         ce.setHeight(h);
3972         ce.dom.scrollTop = 0;
3973     },
3974     /**
3975      * Collapse the navbar pulldown 
3976      */
3977     collapse : function()
3978     {
3979          var ce = this.el.select('.navbar-collapse',true).first();
3980        
3981         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
3982             // it's collapsed or collapsing..
3983             return;
3984         }
3985         ce.removeClass('in'); // old...
3986         ce.setHeight(ce.getHeight());
3987         ce.removeClass('show');
3988         ce.addClass('collapsing');
3989         
3990         ce.on('transitionend', function() {
3991             ce.dom.style.height = '';
3992             ce.removeClass('collapsing');
3993             ce.addClass('collapse');
3994         }, this, { single: true} );
3995         ce.setHeight(0);
3996     }
3997     
3998     
3999     
4000 });
4001
4002
4003
4004  
4005
4006  /*
4007  * - LGPL
4008  *
4009  * navbar
4010  * 
4011  */
4012
4013 /**
4014  * @class Roo.bootstrap.NavSimplebar
4015  * @extends Roo.bootstrap.Navbar
4016  * Bootstrap Sidebar class
4017  *
4018  * @cfg {Boolean} inverse is inverted color
4019  * 
4020  * @cfg {String} type (nav | pills | tabs)
4021  * @cfg {Boolean} arrangement stacked | justified
4022  * @cfg {String} align (left | right) alignment
4023  * 
4024  * @cfg {Boolean} main (true|false) main nav bar? default false
4025  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4026  * 
4027  * @cfg {String} tag (header|footer|nav|div) default is nav 
4028
4029  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4030  * 
4031  * 
4032  * @constructor
4033  * Create a new Sidebar
4034  * @param {Object} config The config object
4035  */
4036
4037
4038 Roo.bootstrap.NavSimplebar = function(config){
4039     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4040 };
4041
4042 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4043     
4044     inverse: false,
4045     
4046     type: false,
4047     arrangement: '',
4048     align : false,
4049     
4050     weight : 'light',
4051     
4052     main : false,
4053     
4054     
4055     tag : false,
4056     
4057     
4058     getAutoCreate : function(){
4059         
4060         
4061         var cfg = {
4062             tag : this.tag || 'div',
4063             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
4064         };
4065         if (['light','white'].indexOf(this.weight) > -1) {
4066             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4067         }
4068         cfg.cls += ' bg-' + this.weight;
4069         
4070         if (this.inverse) {
4071             cfg.cls += ' navbar-inverse';
4072             
4073         }
4074         
4075         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4076         
4077         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
4078             return cfg;
4079         }
4080         
4081         
4082     
4083         
4084         cfg.cn = [
4085             {
4086                 cls: 'nav nav-' + this.xtype,
4087                 tag : 'ul'
4088             }
4089         ];
4090         
4091          
4092         this.type = this.type || 'nav';
4093         if (['tabs','pills'].indexOf(this.type) != -1) {
4094             cfg.cn[0].cls += ' nav-' + this.type
4095         
4096         
4097         } else {
4098             if (this.type!=='nav') {
4099                 Roo.log('nav type must be nav/tabs/pills')
4100             }
4101             cfg.cn[0].cls += ' navbar-nav'
4102         }
4103         
4104         
4105         
4106         
4107         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4108             cfg.cn[0].cls += ' nav-' + this.arrangement;
4109         }
4110         
4111         
4112         if (this.align === 'right') {
4113             cfg.cn[0].cls += ' navbar-right';
4114         }
4115         
4116         
4117         
4118         
4119         return cfg;
4120     
4121         
4122     }
4123     
4124     
4125     
4126 });
4127
4128
4129
4130  
4131
4132  
4133        /*
4134  * - LGPL
4135  *
4136  * navbar
4137  * navbar-fixed-top
4138  * navbar-expand-md  fixed-top 
4139  */
4140
4141 /**
4142  * @class Roo.bootstrap.NavHeaderbar
4143  * @extends Roo.bootstrap.NavSimplebar
4144  * Bootstrap Sidebar class
4145  *
4146  * @cfg {String} brand what is brand
4147  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4148  * @cfg {String} brand_href href of the brand
4149  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4150  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4151  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4152  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4153  * 
4154  * @constructor
4155  * Create a new Sidebar
4156  * @param {Object} config The config object
4157  */
4158
4159
4160 Roo.bootstrap.NavHeaderbar = function(config){
4161     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4162       
4163 };
4164
4165 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4166     
4167     position: '',
4168     brand: '',
4169     brand_href: false,
4170     srButton : true,
4171     autohide : false,
4172     desktopCenter : false,
4173    
4174     
4175     getAutoCreate : function(){
4176         
4177         var   cfg = {
4178             tag: this.nav || 'nav',
4179             cls: 'navbar navbar-expand-md',
4180             role: 'navigation',
4181             cn: []
4182         };
4183         
4184         var cn = cfg.cn;
4185         if (this.desktopCenter) {
4186             cn.push({cls : 'container', cn : []});
4187             cn = cn[0].cn;
4188         }
4189         
4190         if(this.srButton){
4191             var btn = {
4192                 tag: 'button',
4193                 type: 'button',
4194                 cls: 'navbar-toggle navbar-toggler',
4195                 'data-toggle': 'collapse',
4196                 cn: [
4197                     {
4198                         tag: 'span',
4199                         cls: 'sr-only',
4200                         html: 'Toggle navigation'
4201                     },
4202                     {
4203                         tag: 'span',
4204                         cls: 'icon-bar navbar-toggler-icon'
4205                     },
4206                     {
4207                         tag: 'span',
4208                         cls: 'icon-bar'
4209                     },
4210                     {
4211                         tag: 'span',
4212                         cls: 'icon-bar'
4213                     }
4214                 ]
4215             };
4216             
4217             cn.push( Roo.bootstrap.version == 4 ? btn : {
4218                 tag: 'div',
4219                 cls: 'navbar-header',
4220                 cn: [
4221                     btn
4222                 ]
4223             });
4224         }
4225         
4226         cn.push({
4227             tag: 'div',
4228             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
4229             cn : []
4230         });
4231         
4232         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4233         
4234         if (['light','white'].indexOf(this.weight) > -1) {
4235             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4236         }
4237         cfg.cls += ' bg-' + this.weight;
4238         
4239         
4240         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4241             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4242             
4243             // tag can override this..
4244             
4245             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4246         }
4247         
4248         if (this.brand !== '') {
4249             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4250             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4251                 tag: 'a',
4252                 href: this.brand_href ? this.brand_href : '#',
4253                 cls: 'navbar-brand',
4254                 cn: [
4255                 this.brand
4256                 ]
4257             });
4258         }
4259         
4260         if(this.main){
4261             cfg.cls += ' main-nav';
4262         }
4263         
4264         
4265         return cfg;
4266
4267         
4268     },
4269     getHeaderChildContainer : function()
4270     {
4271         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4272             return this.el.select('.navbar-header',true).first();
4273         }
4274         
4275         return this.getChildContainer();
4276     },
4277     
4278     
4279     initEvents : function()
4280     {
4281         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4282         
4283         if (this.autohide) {
4284             
4285             var prevScroll = 0;
4286             var ft = this.el;
4287             
4288             Roo.get(document).on('scroll',function(e) {
4289                 var ns = Roo.get(document).getScroll().top;
4290                 var os = prevScroll;
4291                 prevScroll = ns;
4292                 
4293                 if(ns > os){
4294                     ft.removeClass('slideDown');
4295                     ft.addClass('slideUp');
4296                     return;
4297                 }
4298                 ft.removeClass('slideUp');
4299                 ft.addClass('slideDown');
4300                  
4301               
4302           },this);
4303         }
4304     }    
4305     
4306 });
4307
4308
4309
4310  
4311
4312  /*
4313  * - LGPL
4314  *
4315  * navbar
4316  * 
4317  */
4318
4319 /**
4320  * @class Roo.bootstrap.NavSidebar
4321  * @extends Roo.bootstrap.Navbar
4322  * Bootstrap Sidebar class
4323  * 
4324  * @constructor
4325  * Create a new Sidebar
4326  * @param {Object} config The config object
4327  */
4328
4329
4330 Roo.bootstrap.NavSidebar = function(config){
4331     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4332 };
4333
4334 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4335     
4336     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4337     
4338     getAutoCreate : function(){
4339         
4340         
4341         return  {
4342             tag: 'div',
4343             cls: 'sidebar sidebar-nav'
4344         };
4345     
4346         
4347     }
4348     
4349     
4350     
4351 });
4352
4353
4354
4355  
4356
4357  /*
4358  * - LGPL
4359  *
4360  * nav group
4361  * 
4362  */
4363
4364 /**
4365  * @class Roo.bootstrap.NavGroup
4366  * @extends Roo.bootstrap.Component
4367  * Bootstrap NavGroup class
4368  * @cfg {String} align (left|right)
4369  * @cfg {Boolean} inverse
4370  * @cfg {String} type (nav|pills|tab) default nav
4371  * @cfg {String} navId - reference Id for navbar.
4372
4373  * 
4374  * @constructor
4375  * Create a new nav group
4376  * @param {Object} config The config object
4377  */
4378
4379 Roo.bootstrap.NavGroup = function(config){
4380     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4381     this.navItems = [];
4382    
4383     Roo.bootstrap.NavGroup.register(this);
4384      this.addEvents({
4385         /**
4386              * @event changed
4387              * Fires when the active item changes
4388              * @param {Roo.bootstrap.NavGroup} this
4389              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4390              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4391          */
4392         'changed': true
4393      });
4394     
4395 };
4396
4397 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4398     
4399     align: '',
4400     inverse: false,
4401     form: false,
4402     type: 'nav',
4403     navId : '',
4404     // private
4405     
4406     navItems : false, 
4407     
4408     getAutoCreate : function()
4409     {
4410         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4411         
4412         cfg = {
4413             tag : 'ul',
4414             cls: 'nav' 
4415         };
4416         if (Roo.bootstrap.version == 4) {
4417             if (['tabs','pills'].indexOf(this.type) != -1) {
4418                 cfg.cls += ' nav-' + this.type; 
4419             } else {
4420                 // trying to remove so header bar can right align top?
4421                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
4422                     // do not use on header bar... 
4423                     cfg.cls += ' navbar-nav';
4424                 }
4425             }
4426             
4427         } else {
4428             if (['tabs','pills'].indexOf(this.type) != -1) {
4429                 cfg.cls += ' nav-' + this.type
4430             } else {
4431                 if (this.type !== 'nav') {
4432                     Roo.log('nav type must be nav/tabs/pills')
4433                 }
4434                 cfg.cls += ' navbar-nav'
4435             }
4436         }
4437         
4438         if (this.parent() && this.parent().sidebar) {
4439             cfg = {
4440                 tag: 'ul',
4441                 cls: 'dashboard-menu sidebar-menu'
4442             };
4443             
4444             return cfg;
4445         }
4446         
4447         if (this.form === true) {
4448             cfg = {
4449                 tag: 'form',
4450                 cls: 'navbar-form form-inline'
4451             };
4452             //nav navbar-right ml-md-auto
4453             if (this.align === 'right') {
4454                 cfg.cls += ' navbar-right ml-md-auto';
4455             } else {
4456                 cfg.cls += ' navbar-left';
4457             }
4458         }
4459         
4460         if (this.align === 'right') {
4461             cfg.cls += ' navbar-right ml-md-auto';
4462         } else {
4463             cfg.cls += ' mr-auto';
4464         }
4465         
4466         if (this.inverse) {
4467             cfg.cls += ' navbar-inverse';
4468             
4469         }
4470         
4471         
4472         return cfg;
4473     },
4474     /**
4475     * sets the active Navigation item
4476     * @param {Roo.bootstrap.NavItem} the new current navitem
4477     */
4478     setActiveItem : function(item)
4479     {
4480         var prev = false;
4481         Roo.each(this.navItems, function(v){
4482             if (v == item) {
4483                 return ;
4484             }
4485             if (v.isActive()) {
4486                 v.setActive(false, true);
4487                 prev = v;
4488                 
4489             }
4490             
4491         });
4492
4493         item.setActive(true, true);
4494         this.fireEvent('changed', this, item, prev);
4495         
4496         
4497     },
4498     /**
4499     * gets the active Navigation item
4500     * @return {Roo.bootstrap.NavItem} the current navitem
4501     */
4502     getActive : function()
4503     {
4504         
4505         var prev = false;
4506         Roo.each(this.navItems, function(v){
4507             
4508             if (v.isActive()) {
4509                 prev = v;
4510                 
4511             }
4512             
4513         });
4514         return prev;
4515     },
4516     
4517     indexOfNav : function()
4518     {
4519         
4520         var prev = false;
4521         Roo.each(this.navItems, function(v,i){
4522             
4523             if (v.isActive()) {
4524                 prev = i;
4525                 
4526             }
4527             
4528         });
4529         return prev;
4530     },
4531     /**
4532     * adds a Navigation item
4533     * @param {Roo.bootstrap.NavItem} the navitem to add
4534     */
4535     addItem : function(cfg)
4536     {
4537         if (this.form && Roo.bootstrap.version == 4) {
4538             cfg.tag = 'div';
4539         }
4540         var cn = new Roo.bootstrap.NavItem(cfg);
4541         this.register(cn);
4542         cn.parentId = this.id;
4543         cn.onRender(this.el, null);
4544         return cn;
4545     },
4546     /**
4547     * register a Navigation item
4548     * @param {Roo.bootstrap.NavItem} the navitem to add
4549     */
4550     register : function(item)
4551     {
4552         this.navItems.push( item);
4553         item.navId = this.navId;
4554     
4555     },
4556     
4557     /**
4558     * clear all the Navigation item
4559     */
4560    
4561     clearAll : function()
4562     {
4563         this.navItems = [];
4564         this.el.dom.innerHTML = '';
4565     },
4566     
4567     getNavItem: function(tabId)
4568     {
4569         var ret = false;
4570         Roo.each(this.navItems, function(e) {
4571             if (e.tabId == tabId) {
4572                ret =  e;
4573                return false;
4574             }
4575             return true;
4576             
4577         });
4578         return ret;
4579     },
4580     
4581     setActiveNext : function()
4582     {
4583         var i = this.indexOfNav(this.getActive());
4584         if (i > this.navItems.length) {
4585             return;
4586         }
4587         this.setActiveItem(this.navItems[i+1]);
4588     },
4589     setActivePrev : function()
4590     {
4591         var i = this.indexOfNav(this.getActive());
4592         if (i  < 1) {
4593             return;
4594         }
4595         this.setActiveItem(this.navItems[i-1]);
4596     },
4597     clearWasActive : function(except) {
4598         Roo.each(this.navItems, function(e) {
4599             if (e.tabId != except.tabId && e.was_active) {
4600                e.was_active = false;
4601                return false;
4602             }
4603             return true;
4604             
4605         });
4606     },
4607     getWasActive : function ()
4608     {
4609         var r = false;
4610         Roo.each(this.navItems, function(e) {
4611             if (e.was_active) {
4612                r = e;
4613                return false;
4614             }
4615             return true;
4616             
4617         });
4618         return r;
4619     }
4620     
4621     
4622 });
4623
4624  
4625 Roo.apply(Roo.bootstrap.NavGroup, {
4626     
4627     groups: {},
4628      /**
4629     * register a Navigation Group
4630     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4631     */
4632     register : function(navgrp)
4633     {
4634         this.groups[navgrp.navId] = navgrp;
4635         
4636     },
4637     /**
4638     * fetch a Navigation Group based on the navigation ID
4639     * @param {string} the navgroup to add
4640     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4641     */
4642     get: function(navId) {
4643         if (typeof(this.groups[navId]) == 'undefined') {
4644             return false;
4645             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4646         }
4647         return this.groups[navId] ;
4648     }
4649     
4650     
4651     
4652 });
4653
4654  /*
4655  * - LGPL
4656  *
4657  * row
4658  * 
4659  */
4660
4661 /**
4662  * @class Roo.bootstrap.NavItem
4663  * @extends Roo.bootstrap.Component
4664  * Bootstrap Navbar.NavItem class
4665  * @cfg {String} href  link to
4666  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4667
4668  * @cfg {String} html content of button
4669  * @cfg {String} badge text inside badge
4670  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4671  * @cfg {String} glyphicon DEPRICATED - use fa
4672  * @cfg {String} icon DEPRICATED - use fa
4673  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4674  * @cfg {Boolean} active Is item active
4675  * @cfg {Boolean} disabled Is item disabled
4676  
4677  * @cfg {Boolean} preventDefault (true | false) default false
4678  * @cfg {String} tabId the tab that this item activates.
4679  * @cfg {String} tagtype (a|span) render as a href or span?
4680  * @cfg {Boolean} animateRef (true|false) link to element default false  
4681   
4682  * @constructor
4683  * Create a new Navbar Item
4684  * @param {Object} config The config object
4685  */
4686 Roo.bootstrap.NavItem = function(config){
4687     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4688     this.addEvents({
4689         // raw events
4690         /**
4691          * @event click
4692          * The raw click event for the entire grid.
4693          * @param {Roo.EventObject} e
4694          */
4695         "click" : true,
4696          /**
4697             * @event changed
4698             * Fires when the active item active state changes
4699             * @param {Roo.bootstrap.NavItem} this
4700             * @param {boolean} state the new state
4701              
4702          */
4703         'changed': true,
4704         /**
4705             * @event scrollto
4706             * Fires when scroll to element
4707             * @param {Roo.bootstrap.NavItem} this
4708             * @param {Object} options
4709             * @param {Roo.EventObject} e
4710              
4711          */
4712         'scrollto': true
4713     });
4714    
4715 };
4716
4717 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4718     
4719     href: false,
4720     html: '',
4721     badge: '',
4722     icon: false,
4723     fa : false,
4724     glyphicon: false,
4725     active: false,
4726     preventDefault : false,
4727     tabId : false,
4728     tagtype : 'a',
4729     tag: 'li',
4730     disabled : false,
4731     animateRef : false,
4732     was_active : false,
4733     button_weight : '',
4734     button_outline : false,
4735     
4736     navLink: false,
4737     
4738     getAutoCreate : function(){
4739          
4740         var cfg = {
4741             tag: this.tag,
4742             cls: 'nav-item'
4743         };
4744         
4745         if (this.active) {
4746             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4747         }
4748         if (this.disabled) {
4749             cfg.cls += ' disabled';
4750         }
4751         
4752         // BS4 only?
4753         if (this.button_weight.length) {
4754             cfg.tag = this.href ? 'a' : 'button';
4755             cfg.html = this.html || '';
4756             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4757             if (this.href) {
4758                 cfg.href = this.href;
4759             }
4760             if (this.fa) {
4761                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
4762             }
4763             
4764             // menu .. should add dropdown-menu class - so no need for carat..
4765             
4766             if (this.badge !== '') {
4767                  
4768                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4769             }
4770             return cfg;
4771         }
4772         
4773         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4774             cfg.cn = [
4775                 {
4776                     tag: this.tagtype,
4777                     href : this.href || "#",
4778                     html: this.html || ''
4779                 }
4780             ];
4781             if (this.tagtype == 'a') {
4782                 cfg.cn[0].cls = 'nav-link';
4783             }
4784             if (this.icon) {
4785                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
4786             }
4787             if (this.fa) {
4788                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
4789             }
4790             if(this.glyphicon) {
4791                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4792             }
4793             
4794             if (this.menu) {
4795                 
4796                 cfg.cn[0].html += " <span class='caret'></span>";
4797              
4798             }
4799             
4800             if (this.badge !== '') {
4801                  
4802                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4803             }
4804         }
4805         
4806         
4807         
4808         return cfg;
4809     },
4810     onRender : function(ct, position)
4811     {
4812        // Roo.log("Call onRender: " + this.xtype);
4813         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4814             this.tag = 'div';
4815         }
4816         
4817         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4818         this.navLink = this.el.select('.nav-link',true).first();
4819         return ret;
4820     },
4821       
4822     
4823     initEvents: function() 
4824     {
4825         if (typeof (this.menu) != 'undefined') {
4826             this.menu.parentType = this.xtype;
4827             this.menu.triggerEl = this.el;
4828             this.menu = this.addxtype(Roo.apply({}, this.menu));
4829         }
4830         
4831         this.el.select('a',true).on('click', this.onClick, this);
4832         
4833         if(this.tagtype == 'span'){
4834             this.el.select('span',true).on('click', this.onClick, this);
4835         }
4836        
4837         // at this point parent should be available..
4838         this.parent().register(this);
4839     },
4840     
4841     onClick : function(e)
4842     {
4843         if (e.getTarget('.dropdown-menu-item')) {
4844             // did you click on a menu itemm.... - then don't trigger onclick..
4845             return;
4846         }
4847         
4848         if(
4849                 this.preventDefault || 
4850                 this.href == '#' 
4851         ){
4852             Roo.log("NavItem - prevent Default?");
4853             e.preventDefault();
4854         }
4855         
4856         if (this.disabled) {
4857             return;
4858         }
4859         
4860         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4861         if (tg && tg.transition) {
4862             Roo.log("waiting for the transitionend");
4863             return;
4864         }
4865         
4866         
4867         
4868         //Roo.log("fire event clicked");
4869         if(this.fireEvent('click', this, e) === false){
4870             return;
4871         };
4872         
4873         if(this.tagtype == 'span'){
4874             return;
4875         }
4876         
4877         //Roo.log(this.href);
4878         var ael = this.el.select('a',true).first();
4879         //Roo.log(ael);
4880         
4881         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4882             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4883             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4884                 return; // ignore... - it's a 'hash' to another page.
4885             }
4886             Roo.log("NavItem - prevent Default?");
4887             e.preventDefault();
4888             this.scrollToElement(e);
4889         }
4890         
4891         
4892         var p =  this.parent();
4893    
4894         if (['tabs','pills'].indexOf(p.type)!==-1) {
4895             if (typeof(p.setActiveItem) !== 'undefined') {
4896                 p.setActiveItem(this);
4897             }
4898         }
4899         
4900         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4901         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4902             // remove the collapsed menu expand...
4903             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
4904         }
4905     },
4906     
4907     isActive: function () {
4908         return this.active
4909     },
4910     setActive : function(state, fire, is_was_active)
4911     {
4912         if (this.active && !state && this.navId) {
4913             this.was_active = true;
4914             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4915             if (nv) {
4916                 nv.clearWasActive(this);
4917             }
4918             
4919         }
4920         this.active = state;
4921         
4922         if (!state ) {
4923             this.el.removeClass('active');
4924             this.navLink ? this.navLink.removeClass('active') : false;
4925         } else if (!this.el.hasClass('active')) {
4926             
4927             this.el.addClass('active');
4928             if (Roo.bootstrap.version == 4 && this.navLink ) {
4929                 this.navLink.addClass('active');
4930             }
4931             
4932         }
4933         if (fire) {
4934             this.fireEvent('changed', this, state);
4935         }
4936         
4937         // show a panel if it's registered and related..
4938         
4939         if (!this.navId || !this.tabId || !state || is_was_active) {
4940             return;
4941         }
4942         
4943         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4944         if (!tg) {
4945             return;
4946         }
4947         var pan = tg.getPanelByName(this.tabId);
4948         if (!pan) {
4949             return;
4950         }
4951         // if we can not flip to new panel - go back to old nav highlight..
4952         if (false == tg.showPanel(pan)) {
4953             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4954             if (nv) {
4955                 var onav = nv.getWasActive();
4956                 if (onav) {
4957                     onav.setActive(true, false, true);
4958                 }
4959             }
4960             
4961         }
4962         
4963         
4964         
4965     },
4966      // this should not be here...
4967     setDisabled : function(state)
4968     {
4969         this.disabled = state;
4970         if (!state ) {
4971             this.el.removeClass('disabled');
4972         } else if (!this.el.hasClass('disabled')) {
4973             this.el.addClass('disabled');
4974         }
4975         
4976     },
4977     
4978     /**
4979      * Fetch the element to display the tooltip on.
4980      * @return {Roo.Element} defaults to this.el
4981      */
4982     tooltipEl : function()
4983     {
4984         return this.el.select('' + this.tagtype + '', true).first();
4985     },
4986     
4987     scrollToElement : function(e)
4988     {
4989         var c = document.body;
4990         
4991         /*
4992          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4993          */
4994         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4995             c = document.documentElement;
4996         }
4997         
4998         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4999         
5000         if(!target){
5001             return;
5002         }
5003
5004         var o = target.calcOffsetsTo(c);
5005         
5006         var options = {
5007             target : target,
5008             value : o[1]
5009         };
5010         
5011         this.fireEvent('scrollto', this, options, e);
5012         
5013         Roo.get(c).scrollTo('top', options.value, true);
5014         
5015         return;
5016     }
5017 });
5018  
5019
5020  /*
5021  * - LGPL
5022  *
5023  * sidebar item
5024  *
5025  *  li
5026  *    <span> icon </span>
5027  *    <span> text </span>
5028  *    <span>badge </span>
5029  */
5030
5031 /**
5032  * @class Roo.bootstrap.NavSidebarItem
5033  * @extends Roo.bootstrap.NavItem
5034  * Bootstrap Navbar.NavSidebarItem class
5035  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5036  * {Boolean} open is the menu open
5037  * {Boolean} buttonView use button as the tigger el rather that a (default false)
5038  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5039  * {String} buttonSize (sm|md|lg)the extra classes for the button
5040  * {Boolean} showArrow show arrow next to the text (default true)
5041  * @constructor
5042  * Create a new Navbar Button
5043  * @param {Object} config The config object
5044  */
5045 Roo.bootstrap.NavSidebarItem = function(config){
5046     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5047     this.addEvents({
5048         // raw events
5049         /**
5050          * @event click
5051          * The raw click event for the entire grid.
5052          * @param {Roo.EventObject} e
5053          */
5054         "click" : true,
5055          /**
5056             * @event changed
5057             * Fires when the active item active state changes
5058             * @param {Roo.bootstrap.NavSidebarItem} this
5059             * @param {boolean} state the new state
5060              
5061          */
5062         'changed': true
5063     });
5064    
5065 };
5066
5067 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
5068     
5069     badgeWeight : 'default',
5070     
5071     open: false,
5072     
5073     buttonView : false,
5074     
5075     buttonWeight : 'default',
5076     
5077     buttonSize : 'md',
5078     
5079     showArrow : true,
5080     
5081     getAutoCreate : function(){
5082         
5083         
5084         var a = {
5085                 tag: 'a',
5086                 href : this.href || '#',
5087                 cls: '',
5088                 html : '',
5089                 cn : []
5090         };
5091         
5092         if(this.buttonView){
5093             a = {
5094                 tag: 'button',
5095                 href : this.href || '#',
5096                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5097                 html : this.html,
5098                 cn : []
5099             };
5100         }
5101         
5102         var cfg = {
5103             tag: 'li',
5104             cls: '',
5105             cn: [ a ]
5106         };
5107         
5108         if (this.active) {
5109             cfg.cls += ' active';
5110         }
5111         
5112         if (this.disabled) {
5113             cfg.cls += ' disabled';
5114         }
5115         if (this.open) {
5116             cfg.cls += ' open x-open';
5117         }
5118         // left icon..
5119         if (this.glyphicon || this.icon) {
5120             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5121             a.cn.push({ tag : 'i', cls : c }) ;
5122         }
5123         
5124         if(!this.buttonView){
5125             var span = {
5126                 tag: 'span',
5127                 html : this.html || ''
5128             };
5129
5130             a.cn.push(span);
5131             
5132         }
5133         
5134         if (this.badge !== '') {
5135             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5136         }
5137         
5138         if (this.menu) {
5139             
5140             if(this.showArrow){
5141                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5142             }
5143             
5144             a.cls += ' dropdown-toggle treeview' ;
5145         }
5146         
5147         return cfg;
5148     },
5149     
5150     initEvents : function()
5151     { 
5152         if (typeof (this.menu) != 'undefined') {
5153             this.menu.parentType = this.xtype;
5154             this.menu.triggerEl = this.el;
5155             this.menu = this.addxtype(Roo.apply({}, this.menu));
5156         }
5157         
5158         this.el.on('click', this.onClick, this);
5159         
5160         if(this.badge !== ''){
5161             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5162         }
5163         
5164     },
5165     
5166     onClick : function(e)
5167     {
5168         if(this.disabled){
5169             e.preventDefault();
5170             return;
5171         }
5172         
5173         if(this.preventDefault){
5174             e.preventDefault();
5175         }
5176         
5177         this.fireEvent('click', this, e);
5178     },
5179     
5180     disable : function()
5181     {
5182         this.setDisabled(true);
5183     },
5184     
5185     enable : function()
5186     {
5187         this.setDisabled(false);
5188     },
5189     
5190     setDisabled : function(state)
5191     {
5192         if(this.disabled == state){
5193             return;
5194         }
5195         
5196         this.disabled = state;
5197         
5198         if (state) {
5199             this.el.addClass('disabled');
5200             return;
5201         }
5202         
5203         this.el.removeClass('disabled');
5204         
5205         return;
5206     },
5207     
5208     setActive : function(state)
5209     {
5210         if(this.active == state){
5211             return;
5212         }
5213         
5214         this.active = state;
5215         
5216         if (state) {
5217             this.el.addClass('active');
5218             return;
5219         }
5220         
5221         this.el.removeClass('active');
5222         
5223         return;
5224     },
5225     
5226     isActive: function () 
5227     {
5228         return this.active;
5229     },
5230     
5231     setBadge : function(str)
5232     {
5233         if(!this.badgeEl){
5234             return;
5235         }
5236         
5237         this.badgeEl.dom.innerHTML = str;
5238     }
5239     
5240    
5241      
5242  
5243 });
5244  
5245
5246  /*
5247  * - LGPL
5248  *
5249  * row
5250  * 
5251  */
5252
5253 /**
5254  * @class Roo.bootstrap.Row
5255  * @extends Roo.bootstrap.Component
5256  * Bootstrap Row class (contains columns...)
5257  * 
5258  * @constructor
5259  * Create a new Row
5260  * @param {Object} config The config object
5261  */
5262
5263 Roo.bootstrap.Row = function(config){
5264     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5265 };
5266
5267 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5268     
5269     getAutoCreate : function(){
5270        return {
5271             cls: 'row clearfix'
5272        };
5273     }
5274     
5275     
5276 });
5277
5278  
5279
5280  /*
5281  * - LGPL
5282  *
5283  * element
5284  * 
5285  */
5286
5287 /**
5288  * @class Roo.bootstrap.Element
5289  * @extends Roo.bootstrap.Component
5290  * Bootstrap Element class
5291  * @cfg {String} html contents of the element
5292  * @cfg {String} tag tag of the element
5293  * @cfg {String} cls class of the element
5294  * @cfg {Boolean} preventDefault (true|false) default false
5295  * @cfg {Boolean} clickable (true|false) default false
5296  * 
5297  * @constructor
5298  * Create a new Element
5299  * @param {Object} config The config object
5300  */
5301
5302 Roo.bootstrap.Element = function(config){
5303     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5304     
5305     this.addEvents({
5306         // raw events
5307         /**
5308          * @event click
5309          * When a element is chick
5310          * @param {Roo.bootstrap.Element} this
5311          * @param {Roo.EventObject} e
5312          */
5313         "click" : true
5314     });
5315 };
5316
5317 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5318     
5319     tag: 'div',
5320     cls: '',
5321     html: '',
5322     preventDefault: false, 
5323     clickable: false,
5324     
5325     getAutoCreate : function(){
5326         
5327         var cfg = {
5328             tag: this.tag,
5329             // cls: this.cls, double assign in parent class Component.js :: onRender
5330             html: this.html
5331         };
5332         
5333         return cfg;
5334     },
5335     
5336     initEvents: function() 
5337     {
5338         Roo.bootstrap.Element.superclass.initEvents.call(this);
5339         
5340         if(this.clickable){
5341             this.el.on('click', this.onClick, this);
5342         }
5343         
5344     },
5345     
5346     onClick : function(e)
5347     {
5348         if(this.preventDefault){
5349             e.preventDefault();
5350         }
5351         
5352         this.fireEvent('click', this, e);
5353     },
5354     
5355     getValue : function()
5356     {
5357         return this.el.dom.innerHTML;
5358     },
5359     
5360     setValue : function(value)
5361     {
5362         this.el.dom.innerHTML = value;
5363     }
5364    
5365 });
5366
5367  
5368
5369  /*
5370  * - LGPL
5371  *
5372  * pagination
5373  * 
5374  */
5375
5376 /**
5377  * @class Roo.bootstrap.Pagination
5378  * @extends Roo.bootstrap.Component
5379  * Bootstrap Pagination class
5380  * @cfg {String} size xs | sm | md | lg
5381  * @cfg {Boolean} inverse false | true
5382  * 
5383  * @constructor
5384  * Create a new Pagination
5385  * @param {Object} config The config object
5386  */
5387
5388 Roo.bootstrap.Pagination = function(config){
5389     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5390 };
5391
5392 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5393     
5394     cls: false,
5395     size: false,
5396     inverse: false,
5397     
5398     getAutoCreate : function(){
5399         var cfg = {
5400             tag: 'ul',
5401                 cls: 'pagination'
5402         };
5403         if (this.inverse) {
5404             cfg.cls += ' inverse';
5405         }
5406         if (this.html) {
5407             cfg.html=this.html;
5408         }
5409         if (this.cls) {
5410             cfg.cls += " " + this.cls;
5411         }
5412         return cfg;
5413     }
5414    
5415 });
5416
5417  
5418
5419  /*
5420  * - LGPL
5421  *
5422  * Pagination item
5423  * 
5424  */
5425
5426
5427 /**
5428  * @class Roo.bootstrap.PaginationItem
5429  * @extends Roo.bootstrap.Component
5430  * Bootstrap PaginationItem class
5431  * @cfg {String} html text
5432  * @cfg {String} href the link
5433  * @cfg {Boolean} preventDefault (true | false) default true
5434  * @cfg {Boolean} active (true | false) default false
5435  * @cfg {Boolean} disabled default false
5436  * 
5437  * 
5438  * @constructor
5439  * Create a new PaginationItem
5440  * @param {Object} config The config object
5441  */
5442
5443
5444 Roo.bootstrap.PaginationItem = function(config){
5445     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5446     this.addEvents({
5447         // raw events
5448         /**
5449          * @event click
5450          * The raw click event for the entire grid.
5451          * @param {Roo.EventObject} e
5452          */
5453         "click" : true
5454     });
5455 };
5456
5457 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5458     
5459     href : false,
5460     html : false,
5461     preventDefault: true,
5462     active : false,
5463     cls : false,
5464     disabled: false,
5465     
5466     getAutoCreate : function(){
5467         var cfg= {
5468             tag: 'li',
5469             cn: [
5470                 {
5471                     tag : 'a',
5472                     href : this.href ? this.href : '#',
5473                     html : this.html ? this.html : ''
5474                 }
5475             ]
5476         };
5477         
5478         if(this.cls){
5479             cfg.cls = this.cls;
5480         }
5481         
5482         if(this.disabled){
5483             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5484         }
5485         
5486         if(this.active){
5487             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5488         }
5489         
5490         return cfg;
5491     },
5492     
5493     initEvents: function() {
5494         
5495         this.el.on('click', this.onClick, this);
5496         
5497     },
5498     onClick : function(e)
5499     {
5500         Roo.log('PaginationItem on click ');
5501         if(this.preventDefault){
5502             e.preventDefault();
5503         }
5504         
5505         if(this.disabled){
5506             return;
5507         }
5508         
5509         this.fireEvent('click', this, e);
5510     }
5511    
5512 });
5513
5514  
5515
5516  /*
5517  * - LGPL
5518  *
5519  * slider
5520  * 
5521  */
5522
5523
5524 /**
5525  * @class Roo.bootstrap.Slider
5526  * @extends Roo.bootstrap.Component
5527  * Bootstrap Slider class
5528  *    
5529  * @constructor
5530  * Create a new Slider
5531  * @param {Object} config The config object
5532  */
5533
5534 Roo.bootstrap.Slider = function(config){
5535     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5536 };
5537
5538 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5539     
5540     getAutoCreate : function(){
5541         
5542         var cfg = {
5543             tag: 'div',
5544             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5545             cn: [
5546                 {
5547                     tag: 'a',
5548                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5549                 }
5550             ]
5551         };
5552         
5553         return cfg;
5554     }
5555    
5556 });
5557
5558  /*
5559  * Based on:
5560  * Ext JS Library 1.1.1
5561  * Copyright(c) 2006-2007, Ext JS, LLC.
5562  *
5563  * Originally Released Under LGPL - original licence link has changed is not relivant.
5564  *
5565  * Fork - LGPL
5566  * <script type="text/javascript">
5567  */
5568  
5569
5570 /**
5571  * @class Roo.grid.ColumnModel
5572  * @extends Roo.util.Observable
5573  * This is the default implementation of a ColumnModel used by the Grid. It defines
5574  * the columns in the grid.
5575  * <br>Usage:<br>
5576  <pre><code>
5577  var colModel = new Roo.grid.ColumnModel([
5578         {header: "Ticker", width: 60, sortable: true, locked: true},
5579         {header: "Company Name", width: 150, sortable: true},
5580         {header: "Market Cap.", width: 100, sortable: true},
5581         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5582         {header: "Employees", width: 100, sortable: true, resizable: false}
5583  ]);
5584  </code></pre>
5585  * <p>
5586  
5587  * The config options listed for this class are options which may appear in each
5588  * individual column definition.
5589  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5590  * @constructor
5591  * @param {Object} config An Array of column config objects. See this class's
5592  * config objects for details.
5593 */
5594 Roo.grid.ColumnModel = function(config){
5595         /**
5596      * The config passed into the constructor
5597      */
5598     this.config = config;
5599     this.lookup = {};
5600
5601     // if no id, create one
5602     // if the column does not have a dataIndex mapping,
5603     // map it to the order it is in the config
5604     for(var i = 0, len = config.length; i < len; i++){
5605         var c = config[i];
5606         if(typeof c.dataIndex == "undefined"){
5607             c.dataIndex = i;
5608         }
5609         if(typeof c.renderer == "string"){
5610             c.renderer = Roo.util.Format[c.renderer];
5611         }
5612         if(typeof c.id == "undefined"){
5613             c.id = Roo.id();
5614         }
5615         if(c.editor && c.editor.xtype){
5616             c.editor  = Roo.factory(c.editor, Roo.grid);
5617         }
5618         if(c.editor && c.editor.isFormField){
5619             c.editor = new Roo.grid.GridEditor(c.editor);
5620         }
5621         this.lookup[c.id] = c;
5622     }
5623
5624     /**
5625      * The width of columns which have no width specified (defaults to 100)
5626      * @type Number
5627      */
5628     this.defaultWidth = 100;
5629
5630     /**
5631      * Default sortable of columns which have no sortable specified (defaults to false)
5632      * @type Boolean
5633      */
5634     this.defaultSortable = false;
5635
5636     this.addEvents({
5637         /**
5638              * @event widthchange
5639              * Fires when the width of a column changes.
5640              * @param {ColumnModel} this
5641              * @param {Number} columnIndex The column index
5642              * @param {Number} newWidth The new width
5643              */
5644             "widthchange": true,
5645         /**
5646              * @event headerchange
5647              * Fires when the text of a header changes.
5648              * @param {ColumnModel} this
5649              * @param {Number} columnIndex The column index
5650              * @param {Number} newText The new header text
5651              */
5652             "headerchange": true,
5653         /**
5654              * @event hiddenchange
5655              * Fires when a column is hidden or "unhidden".
5656              * @param {ColumnModel} this
5657              * @param {Number} columnIndex The column index
5658              * @param {Boolean} hidden true if hidden, false otherwise
5659              */
5660             "hiddenchange": true,
5661             /**
5662          * @event columnmoved
5663          * Fires when a column is moved.
5664          * @param {ColumnModel} this
5665          * @param {Number} oldIndex
5666          * @param {Number} newIndex
5667          */
5668         "columnmoved" : true,
5669         /**
5670          * @event columlockchange
5671          * Fires when a column's locked state is changed
5672          * @param {ColumnModel} this
5673          * @param {Number} colIndex
5674          * @param {Boolean} locked true if locked
5675          */
5676         "columnlockchange" : true
5677     });
5678     Roo.grid.ColumnModel.superclass.constructor.call(this);
5679 };
5680 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5681     /**
5682      * @cfg {String} header The header text to display in the Grid view.
5683      */
5684     /**
5685      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5686      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5687      * specified, the column's index is used as an index into the Record's data Array.
5688      */
5689     /**
5690      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5691      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5692      */
5693     /**
5694      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5695      * Defaults to the value of the {@link #defaultSortable} property.
5696      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5697      */
5698     /**
5699      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5700      */
5701     /**
5702      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5703      */
5704     /**
5705      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5706      */
5707     /**
5708      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5709      */
5710     /**
5711      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5712      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5713      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5714      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5715      */
5716        /**
5717      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5718      */
5719     /**
5720      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5721      */
5722     /**
5723      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5724      */
5725     /**
5726      * @cfg {String} cursor (Optional)
5727      */
5728     /**
5729      * @cfg {String} tooltip (Optional)
5730      */
5731     /**
5732      * @cfg {Number} xs (Optional)
5733      */
5734     /**
5735      * @cfg {Number} sm (Optional)
5736      */
5737     /**
5738      * @cfg {Number} md (Optional)
5739      */
5740     /**
5741      * @cfg {Number} lg (Optional)
5742      */
5743     /**
5744      * Returns the id of the column at the specified index.
5745      * @param {Number} index The column index
5746      * @return {String} the id
5747      */
5748     getColumnId : function(index){
5749         return this.config[index].id;
5750     },
5751
5752     /**
5753      * Returns the column for a specified id.
5754      * @param {String} id The column id
5755      * @return {Object} the column
5756      */
5757     getColumnById : function(id){
5758         return this.lookup[id];
5759     },
5760
5761     
5762     /**
5763      * Returns the column for a specified dataIndex.
5764      * @param {String} dataIndex The column dataIndex
5765      * @return {Object|Boolean} the column or false if not found
5766      */
5767     getColumnByDataIndex: function(dataIndex){
5768         var index = this.findColumnIndex(dataIndex);
5769         return index > -1 ? this.config[index] : false;
5770     },
5771     
5772     /**
5773      * Returns the index for a specified column id.
5774      * @param {String} id The column id
5775      * @return {Number} the index, or -1 if not found
5776      */
5777     getIndexById : function(id){
5778         for(var i = 0, len = this.config.length; i < len; i++){
5779             if(this.config[i].id == id){
5780                 return i;
5781             }
5782         }
5783         return -1;
5784     },
5785     
5786     /**
5787      * Returns the index for a specified column dataIndex.
5788      * @param {String} dataIndex The column dataIndex
5789      * @return {Number} the index, or -1 if not found
5790      */
5791     
5792     findColumnIndex : function(dataIndex){
5793         for(var i = 0, len = this.config.length; i < len; i++){
5794             if(this.config[i].dataIndex == dataIndex){
5795                 return i;
5796             }
5797         }
5798         return -1;
5799     },
5800     
5801     
5802     moveColumn : function(oldIndex, newIndex){
5803         var c = this.config[oldIndex];
5804         this.config.splice(oldIndex, 1);
5805         this.config.splice(newIndex, 0, c);
5806         this.dataMap = null;
5807         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5808     },
5809
5810     isLocked : function(colIndex){
5811         return this.config[colIndex].locked === true;
5812     },
5813
5814     setLocked : function(colIndex, value, suppressEvent){
5815         if(this.isLocked(colIndex) == value){
5816             return;
5817         }
5818         this.config[colIndex].locked = value;
5819         if(!suppressEvent){
5820             this.fireEvent("columnlockchange", this, colIndex, value);
5821         }
5822     },
5823
5824     getTotalLockedWidth : function(){
5825         var totalWidth = 0;
5826         for(var i = 0; i < this.config.length; i++){
5827             if(this.isLocked(i) && !this.isHidden(i)){
5828                 this.totalWidth += this.getColumnWidth(i);
5829             }
5830         }
5831         return totalWidth;
5832     },
5833
5834     getLockedCount : function(){
5835         for(var i = 0, len = this.config.length; i < len; i++){
5836             if(!this.isLocked(i)){
5837                 return i;
5838             }
5839         }
5840         
5841         return this.config.length;
5842     },
5843
5844     /**
5845      * Returns the number of columns.
5846      * @return {Number}
5847      */
5848     getColumnCount : function(visibleOnly){
5849         if(visibleOnly === true){
5850             var c = 0;
5851             for(var i = 0, len = this.config.length; i < len; i++){
5852                 if(!this.isHidden(i)){
5853                     c++;
5854                 }
5855             }
5856             return c;
5857         }
5858         return this.config.length;
5859     },
5860
5861     /**
5862      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5863      * @param {Function} fn
5864      * @param {Object} scope (optional)
5865      * @return {Array} result
5866      */
5867     getColumnsBy : function(fn, scope){
5868         var r = [];
5869         for(var i = 0, len = this.config.length; i < len; i++){
5870             var c = this.config[i];
5871             if(fn.call(scope||this, c, i) === true){
5872                 r[r.length] = c;
5873             }
5874         }
5875         return r;
5876     },
5877
5878     /**
5879      * Returns true if the specified column is sortable.
5880      * @param {Number} col The column index
5881      * @return {Boolean}
5882      */
5883     isSortable : function(col){
5884         if(typeof this.config[col].sortable == "undefined"){
5885             return this.defaultSortable;
5886         }
5887         return this.config[col].sortable;
5888     },
5889
5890     /**
5891      * Returns the rendering (formatting) function defined for the column.
5892      * @param {Number} col The column index.
5893      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5894      */
5895     getRenderer : function(col){
5896         if(!this.config[col].renderer){
5897             return Roo.grid.ColumnModel.defaultRenderer;
5898         }
5899         return this.config[col].renderer;
5900     },
5901
5902     /**
5903      * Sets the rendering (formatting) function for a column.
5904      * @param {Number} col The column index
5905      * @param {Function} fn The function to use to process the cell's raw data
5906      * to return HTML markup for the grid view. The render function is called with
5907      * the following parameters:<ul>
5908      * <li>Data value.</li>
5909      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5910      * <li>css A CSS style string to apply to the table cell.</li>
5911      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5912      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5913      * <li>Row index</li>
5914      * <li>Column index</li>
5915      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5916      */
5917     setRenderer : function(col, fn){
5918         this.config[col].renderer = fn;
5919     },
5920
5921     /**
5922      * Returns the width for the specified column.
5923      * @param {Number} col The column index
5924      * @return {Number}
5925      */
5926     getColumnWidth : function(col){
5927         return this.config[col].width * 1 || this.defaultWidth;
5928     },
5929
5930     /**
5931      * Sets the width for a column.
5932      * @param {Number} col The column index
5933      * @param {Number} width The new width
5934      */
5935     setColumnWidth : function(col, width, suppressEvent){
5936         this.config[col].width = width;
5937         this.totalWidth = null;
5938         if(!suppressEvent){
5939              this.fireEvent("widthchange", this, col, width);
5940         }
5941     },
5942
5943     /**
5944      * Returns the total width of all columns.
5945      * @param {Boolean} includeHidden True to include hidden column widths
5946      * @return {Number}
5947      */
5948     getTotalWidth : function(includeHidden){
5949         if(!this.totalWidth){
5950             this.totalWidth = 0;
5951             for(var i = 0, len = this.config.length; i < len; i++){
5952                 if(includeHidden || !this.isHidden(i)){
5953                     this.totalWidth += this.getColumnWidth(i);
5954                 }
5955             }
5956         }
5957         return this.totalWidth;
5958     },
5959
5960     /**
5961      * Returns the header for the specified column.
5962      * @param {Number} col The column index
5963      * @return {String}
5964      */
5965     getColumnHeader : function(col){
5966         return this.config[col].header;
5967     },
5968
5969     /**
5970      * Sets the header for a column.
5971      * @param {Number} col The column index
5972      * @param {String} header The new header
5973      */
5974     setColumnHeader : function(col, header){
5975         this.config[col].header = header;
5976         this.fireEvent("headerchange", this, col, header);
5977     },
5978
5979     /**
5980      * Returns the tooltip for the specified column.
5981      * @param {Number} col The column index
5982      * @return {String}
5983      */
5984     getColumnTooltip : function(col){
5985             return this.config[col].tooltip;
5986     },
5987     /**
5988      * Sets the tooltip for a column.
5989      * @param {Number} col The column index
5990      * @param {String} tooltip The new tooltip
5991      */
5992     setColumnTooltip : function(col, tooltip){
5993             this.config[col].tooltip = tooltip;
5994     },
5995
5996     /**
5997      * Returns the dataIndex for the specified column.
5998      * @param {Number} col The column index
5999      * @return {Number}
6000      */
6001     getDataIndex : function(col){
6002         return this.config[col].dataIndex;
6003     },
6004
6005     /**
6006      * Sets the dataIndex for a column.
6007      * @param {Number} col The column index
6008      * @param {Number} dataIndex The new dataIndex
6009      */
6010     setDataIndex : function(col, dataIndex){
6011         this.config[col].dataIndex = dataIndex;
6012     },
6013
6014     
6015     
6016     /**
6017      * Returns true if the cell is editable.
6018      * @param {Number} colIndex The column index
6019      * @param {Number} rowIndex The row index - this is nto actually used..?
6020      * @return {Boolean}
6021      */
6022     isCellEditable : function(colIndex, rowIndex){
6023         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6024     },
6025
6026     /**
6027      * Returns the editor defined for the cell/column.
6028      * return false or null to disable editing.
6029      * @param {Number} colIndex The column index
6030      * @param {Number} rowIndex The row index
6031      * @return {Object}
6032      */
6033     getCellEditor : function(colIndex, rowIndex){
6034         return this.config[colIndex].editor;
6035     },
6036
6037     /**
6038      * Sets if a column is editable.
6039      * @param {Number} col The column index
6040      * @param {Boolean} editable True if the column is editable
6041      */
6042     setEditable : function(col, editable){
6043         this.config[col].editable = editable;
6044     },
6045
6046
6047     /**
6048      * Returns true if the column is hidden.
6049      * @param {Number} colIndex The column index
6050      * @return {Boolean}
6051      */
6052     isHidden : function(colIndex){
6053         return this.config[colIndex].hidden;
6054     },
6055
6056
6057     /**
6058      * Returns true if the column width cannot be changed
6059      */
6060     isFixed : function(colIndex){
6061         return this.config[colIndex].fixed;
6062     },
6063
6064     /**
6065      * Returns true if the column can be resized
6066      * @return {Boolean}
6067      */
6068     isResizable : function(colIndex){
6069         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6070     },
6071     /**
6072      * Sets if a column is hidden.
6073      * @param {Number} colIndex The column index
6074      * @param {Boolean} hidden True if the column is hidden
6075      */
6076     setHidden : function(colIndex, hidden){
6077         this.config[colIndex].hidden = hidden;
6078         this.totalWidth = null;
6079         this.fireEvent("hiddenchange", this, colIndex, hidden);
6080     },
6081
6082     /**
6083      * Sets the editor for a column.
6084      * @param {Number} col The column index
6085      * @param {Object} editor The editor object
6086      */
6087     setEditor : function(col, editor){
6088         this.config[col].editor = editor;
6089     }
6090 });
6091
6092 Roo.grid.ColumnModel.defaultRenderer = function(value)
6093 {
6094     if(typeof value == "object") {
6095         return value;
6096     }
6097         if(typeof value == "string" && value.length < 1){
6098             return "&#160;";
6099         }
6100     
6101         return String.format("{0}", value);
6102 };
6103
6104 // Alias for backwards compatibility
6105 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6106 /*
6107  * Based on:
6108  * Ext JS Library 1.1.1
6109  * Copyright(c) 2006-2007, Ext JS, LLC.
6110  *
6111  * Originally Released Under LGPL - original licence link has changed is not relivant.
6112  *
6113  * Fork - LGPL
6114  * <script type="text/javascript">
6115  */
6116  
6117 /**
6118  * @class Roo.LoadMask
6119  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6120  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6121  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6122  * element's UpdateManager load indicator and will be destroyed after the initial load.
6123  * @constructor
6124  * Create a new LoadMask
6125  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6126  * @param {Object} config The config object
6127  */
6128 Roo.LoadMask = function(el, config){
6129     this.el = Roo.get(el);
6130     Roo.apply(this, config);
6131     if(this.store){
6132         this.store.on('beforeload', this.onBeforeLoad, this);
6133         this.store.on('load', this.onLoad, this);
6134         this.store.on('loadexception', this.onLoadException, this);
6135         this.removeMask = false;
6136     }else{
6137         var um = this.el.getUpdateManager();
6138         um.showLoadIndicator = false; // disable the default indicator
6139         um.on('beforeupdate', this.onBeforeLoad, this);
6140         um.on('update', this.onLoad, this);
6141         um.on('failure', this.onLoad, this);
6142         this.removeMask = true;
6143     }
6144 };
6145
6146 Roo.LoadMask.prototype = {
6147     /**
6148      * @cfg {Boolean} removeMask
6149      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6150      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6151      */
6152     /**
6153      * @cfg {String} msg
6154      * The text to display in a centered loading message box (defaults to 'Loading...')
6155      */
6156     msg : 'Loading...',
6157     /**
6158      * @cfg {String} msgCls
6159      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6160      */
6161     msgCls : 'x-mask-loading',
6162
6163     /**
6164      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6165      * @type Boolean
6166      */
6167     disabled: false,
6168
6169     /**
6170      * Disables the mask to prevent it from being displayed
6171      */
6172     disable : function(){
6173        this.disabled = true;
6174     },
6175
6176     /**
6177      * Enables the mask so that it can be displayed
6178      */
6179     enable : function(){
6180         this.disabled = false;
6181     },
6182     
6183     onLoadException : function()
6184     {
6185         Roo.log(arguments);
6186         
6187         if (typeof(arguments[3]) != 'undefined') {
6188             Roo.MessageBox.alert("Error loading",arguments[3]);
6189         } 
6190         /*
6191         try {
6192             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6193                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6194             }   
6195         } catch(e) {
6196             
6197         }
6198         */
6199     
6200         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6201     },
6202     // private
6203     onLoad : function()
6204     {
6205         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6206     },
6207
6208     // private
6209     onBeforeLoad : function(){
6210         if(!this.disabled){
6211             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6212         }
6213     },
6214
6215     // private
6216     destroy : function(){
6217         if(this.store){
6218             this.store.un('beforeload', this.onBeforeLoad, this);
6219             this.store.un('load', this.onLoad, this);
6220             this.store.un('loadexception', this.onLoadException, this);
6221         }else{
6222             var um = this.el.getUpdateManager();
6223             um.un('beforeupdate', this.onBeforeLoad, this);
6224             um.un('update', this.onLoad, this);
6225             um.un('failure', this.onLoad, this);
6226         }
6227     }
6228 };/*
6229  * - LGPL
6230  *
6231  * table
6232  * 
6233  */
6234
6235 /**
6236  * @class Roo.bootstrap.Table
6237  * @extends Roo.bootstrap.Component
6238  * Bootstrap Table class
6239  * @cfg {String} cls table class
6240  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6241  * @cfg {String} bgcolor Specifies the background color for a table
6242  * @cfg {Number} border Specifies whether the table cells should have borders or not
6243  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6244  * @cfg {Number} cellspacing Specifies the space between cells
6245  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6246  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6247  * @cfg {String} sortable Specifies that the table should be sortable
6248  * @cfg {String} summary Specifies a summary of the content of a table
6249  * @cfg {Number} width Specifies the width of a table
6250  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6251  * 
6252  * @cfg {boolean} striped Should the rows be alternative striped
6253  * @cfg {boolean} bordered Add borders to the table
6254  * @cfg {boolean} hover Add hover highlighting
6255  * @cfg {boolean} condensed Format condensed
6256  * @cfg {boolean} responsive Format condensed
6257  * @cfg {Boolean} loadMask (true|false) default false
6258  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6259  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6260  * @cfg {Boolean} rowSelection (true|false) default false
6261  * @cfg {Boolean} cellSelection (true|false) default false
6262  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6263  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6264  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6265  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6266  
6267  * 
6268  * @constructor
6269  * Create a new Table
6270  * @param {Object} config The config object
6271  */
6272
6273 Roo.bootstrap.Table = function(config){
6274     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6275     
6276   
6277     
6278     // BC...
6279     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6280     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6281     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6282     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6283     
6284     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6285     if (this.sm) {
6286         this.sm.grid = this;
6287         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6288         this.sm = this.selModel;
6289         this.sm.xmodule = this.xmodule || false;
6290     }
6291     
6292     if (this.cm && typeof(this.cm.config) == 'undefined') {
6293         this.colModel = new Roo.grid.ColumnModel(this.cm);
6294         this.cm = this.colModel;
6295         this.cm.xmodule = this.xmodule || false;
6296     }
6297     if (this.store) {
6298         this.store= Roo.factory(this.store, Roo.data);
6299         this.ds = this.store;
6300         this.ds.xmodule = this.xmodule || false;
6301          
6302     }
6303     if (this.footer && this.store) {
6304         this.footer.dataSource = this.ds;
6305         this.footer = Roo.factory(this.footer);
6306     }
6307     
6308     /** @private */
6309     this.addEvents({
6310         /**
6311          * @event cellclick
6312          * Fires when a cell is clicked
6313          * @param {Roo.bootstrap.Table} this
6314          * @param {Roo.Element} el
6315          * @param {Number} rowIndex
6316          * @param {Number} columnIndex
6317          * @param {Roo.EventObject} e
6318          */
6319         "cellclick" : true,
6320         /**
6321          * @event celldblclick
6322          * Fires when a cell is double clicked
6323          * @param {Roo.bootstrap.Table} this
6324          * @param {Roo.Element} el
6325          * @param {Number} rowIndex
6326          * @param {Number} columnIndex
6327          * @param {Roo.EventObject} e
6328          */
6329         "celldblclick" : true,
6330         /**
6331          * @event rowclick
6332          * Fires when a row is clicked
6333          * @param {Roo.bootstrap.Table} this
6334          * @param {Roo.Element} el
6335          * @param {Number} rowIndex
6336          * @param {Roo.EventObject} e
6337          */
6338         "rowclick" : true,
6339         /**
6340          * @event rowdblclick
6341          * Fires when a row is double clicked
6342          * @param {Roo.bootstrap.Table} this
6343          * @param {Roo.Element} el
6344          * @param {Number} rowIndex
6345          * @param {Roo.EventObject} e
6346          */
6347         "rowdblclick" : true,
6348         /**
6349          * @event mouseover
6350          * Fires when a mouseover occur
6351          * @param {Roo.bootstrap.Table} this
6352          * @param {Roo.Element} el
6353          * @param {Number} rowIndex
6354          * @param {Number} columnIndex
6355          * @param {Roo.EventObject} e
6356          */
6357         "mouseover" : true,
6358         /**
6359          * @event mouseout
6360          * Fires when a mouseout occur
6361          * @param {Roo.bootstrap.Table} this
6362          * @param {Roo.Element} el
6363          * @param {Number} rowIndex
6364          * @param {Number} columnIndex
6365          * @param {Roo.EventObject} e
6366          */
6367         "mouseout" : true,
6368         /**
6369          * @event rowclass
6370          * Fires when a row is rendered, so you can change add a style to it.
6371          * @param {Roo.bootstrap.Table} this
6372          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6373          */
6374         'rowclass' : true,
6375           /**
6376          * @event rowsrendered
6377          * Fires when all the  rows have been rendered
6378          * @param {Roo.bootstrap.Table} this
6379          */
6380         'rowsrendered' : true,
6381         /**
6382          * @event contextmenu
6383          * The raw contextmenu event for the entire grid.
6384          * @param {Roo.EventObject} e
6385          */
6386         "contextmenu" : true,
6387         /**
6388          * @event rowcontextmenu
6389          * Fires when a row is right clicked
6390          * @param {Roo.bootstrap.Table} this
6391          * @param {Number} rowIndex
6392          * @param {Roo.EventObject} e
6393          */
6394         "rowcontextmenu" : true,
6395         /**
6396          * @event cellcontextmenu
6397          * Fires when a cell is right clicked
6398          * @param {Roo.bootstrap.Table} this
6399          * @param {Number} rowIndex
6400          * @param {Number} cellIndex
6401          * @param {Roo.EventObject} e
6402          */
6403          "cellcontextmenu" : true,
6404          /**
6405          * @event headercontextmenu
6406          * Fires when a header is right clicked
6407          * @param {Roo.bootstrap.Table} this
6408          * @param {Number} columnIndex
6409          * @param {Roo.EventObject} e
6410          */
6411         "headercontextmenu" : true
6412     });
6413 };
6414
6415 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6416     
6417     cls: false,
6418     align: false,
6419     bgcolor: false,
6420     border: false,
6421     cellpadding: false,
6422     cellspacing: false,
6423     frame: false,
6424     rules: false,
6425     sortable: false,
6426     summary: false,
6427     width: false,
6428     striped : false,
6429     scrollBody : false,
6430     bordered: false,
6431     hover:  false,
6432     condensed : false,
6433     responsive : false,
6434     sm : false,
6435     cm : false,
6436     store : false,
6437     loadMask : false,
6438     footerShow : true,
6439     headerShow : true,
6440   
6441     rowSelection : false,
6442     cellSelection : false,
6443     layout : false,
6444     
6445     // Roo.Element - the tbody
6446     mainBody: false,
6447     // Roo.Element - thead element
6448     mainHead: false,
6449     
6450     container: false, // used by gridpanel...
6451     
6452     lazyLoad : false,
6453     
6454     CSS : Roo.util.CSS,
6455     
6456     auto_hide_footer : false,
6457     
6458     getAutoCreate : function()
6459     {
6460         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6461         
6462         cfg = {
6463             tag: 'table',
6464             cls : 'table',
6465             cn : []
6466         };
6467         if (this.scrollBody) {
6468             cfg.cls += ' table-body-fixed';
6469         }    
6470         if (this.striped) {
6471             cfg.cls += ' table-striped';
6472         }
6473         
6474         if (this.hover) {
6475             cfg.cls += ' table-hover';
6476         }
6477         if (this.bordered) {
6478             cfg.cls += ' table-bordered';
6479         }
6480         if (this.condensed) {
6481             cfg.cls += ' table-condensed';
6482         }
6483         if (this.responsive) {
6484             cfg.cls += ' table-responsive';
6485         }
6486         
6487         if (this.cls) {
6488             cfg.cls+=  ' ' +this.cls;
6489         }
6490         
6491         // this lot should be simplifed...
6492         var _t = this;
6493         var cp = [
6494             'align',
6495             'bgcolor',
6496             'border',
6497             'cellpadding',
6498             'cellspacing',
6499             'frame',
6500             'rules',
6501             'sortable',
6502             'summary',
6503             'width'
6504         ].forEach(function(k) {
6505             if (_t[k]) {
6506                 cfg[k] = _t[k];
6507             }
6508         });
6509         
6510         
6511         if (this.layout) {
6512             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6513         }
6514         
6515         if(this.store || this.cm){
6516             if(this.headerShow){
6517                 cfg.cn.push(this.renderHeader());
6518             }
6519             
6520             cfg.cn.push(this.renderBody());
6521             
6522             if(this.footerShow){
6523                 cfg.cn.push(this.renderFooter());
6524             }
6525             // where does this come from?
6526             //cfg.cls+=  ' TableGrid';
6527         }
6528         
6529         return { cn : [ cfg ] };
6530     },
6531     
6532     initEvents : function()
6533     {   
6534         if(!this.store || !this.cm){
6535             return;
6536         }
6537         if (this.selModel) {
6538             this.selModel.initEvents();
6539         }
6540         
6541         
6542         //Roo.log('initEvents with ds!!!!');
6543         
6544         this.mainBody = this.el.select('tbody', true).first();
6545         this.mainHead = this.el.select('thead', true).first();
6546         this.mainFoot = this.el.select('tfoot', true).first();
6547         
6548         
6549         
6550         var _this = this;
6551         
6552         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6553             e.on('click', _this.sort, _this);
6554         });
6555         
6556         this.mainBody.on("click", this.onClick, this);
6557         this.mainBody.on("dblclick", this.onDblClick, this);
6558         
6559         // why is this done????? = it breaks dialogs??
6560         //this.parent().el.setStyle('position', 'relative');
6561         
6562         
6563         if (this.footer) {
6564             this.footer.parentId = this.id;
6565             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6566             
6567             if(this.lazyLoad){
6568                 this.el.select('tfoot tr td').first().addClass('hide');
6569             }
6570         } 
6571         
6572         if(this.loadMask) {
6573             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6574         }
6575         
6576         this.store.on('load', this.onLoad, this);
6577         this.store.on('beforeload', this.onBeforeLoad, this);
6578         this.store.on('update', this.onUpdate, this);
6579         this.store.on('add', this.onAdd, this);
6580         this.store.on("clear", this.clear, this);
6581         
6582         this.el.on("contextmenu", this.onContextMenu, this);
6583         
6584         this.mainBody.on('scroll', this.onBodyScroll, this);
6585         
6586         this.cm.on("headerchange", this.onHeaderChange, this);
6587         
6588         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6589         
6590     },
6591     
6592     onContextMenu : function(e, t)
6593     {
6594         this.processEvent("contextmenu", e);
6595     },
6596     
6597     processEvent : function(name, e)
6598     {
6599         if (name != 'touchstart' ) {
6600             this.fireEvent(name, e);    
6601         }
6602         
6603         var t = e.getTarget();
6604         
6605         var cell = Roo.get(t);
6606         
6607         if(!cell){
6608             return;
6609         }
6610         
6611         if(cell.findParent('tfoot', false, true)){
6612             return;
6613         }
6614         
6615         if(cell.findParent('thead', false, true)){
6616             
6617             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6618                 cell = Roo.get(t).findParent('th', false, true);
6619                 if (!cell) {
6620                     Roo.log("failed to find th in thead?");
6621                     Roo.log(e.getTarget());
6622                     return;
6623                 }
6624             }
6625             
6626             var cellIndex = cell.dom.cellIndex;
6627             
6628             var ename = name == 'touchstart' ? 'click' : name;
6629             this.fireEvent("header" + ename, this, cellIndex, e);
6630             
6631             return;
6632         }
6633         
6634         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6635             cell = Roo.get(t).findParent('td', false, true);
6636             if (!cell) {
6637                 Roo.log("failed to find th in tbody?");
6638                 Roo.log(e.getTarget());
6639                 return;
6640             }
6641         }
6642         
6643         var row = cell.findParent('tr', false, true);
6644         var cellIndex = cell.dom.cellIndex;
6645         var rowIndex = row.dom.rowIndex - 1;
6646         
6647         if(row !== false){
6648             
6649             this.fireEvent("row" + name, this, rowIndex, e);
6650             
6651             if(cell !== false){
6652             
6653                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6654             }
6655         }
6656         
6657     },
6658     
6659     onMouseover : function(e, el)
6660     {
6661         var cell = Roo.get(el);
6662         
6663         if(!cell){
6664             return;
6665         }
6666         
6667         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6668             cell = cell.findParent('td', false, true);
6669         }
6670         
6671         var row = cell.findParent('tr', false, true);
6672         var cellIndex = cell.dom.cellIndex;
6673         var rowIndex = row.dom.rowIndex - 1; // start from 0
6674         
6675         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6676         
6677     },
6678     
6679     onMouseout : function(e, el)
6680     {
6681         var cell = Roo.get(el);
6682         
6683         if(!cell){
6684             return;
6685         }
6686         
6687         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6688             cell = cell.findParent('td', false, true);
6689         }
6690         
6691         var row = cell.findParent('tr', false, true);
6692         var cellIndex = cell.dom.cellIndex;
6693         var rowIndex = row.dom.rowIndex - 1; // start from 0
6694         
6695         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6696         
6697     },
6698     
6699     onClick : function(e, el)
6700     {
6701         var cell = Roo.get(el);
6702         
6703         if(!cell || (!this.cellSelection && !this.rowSelection)){
6704             return;
6705         }
6706         
6707         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6708             cell = cell.findParent('td', false, true);
6709         }
6710         
6711         if(!cell || typeof(cell) == 'undefined'){
6712             return;
6713         }
6714         
6715         var row = cell.findParent('tr', false, true);
6716         
6717         if(!row || typeof(row) == 'undefined'){
6718             return;
6719         }
6720         
6721         var cellIndex = cell.dom.cellIndex;
6722         var rowIndex = this.getRowIndex(row);
6723         
6724         // why??? - should these not be based on SelectionModel?
6725         if(this.cellSelection){
6726             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6727         }
6728         
6729         if(this.rowSelection){
6730             this.fireEvent('rowclick', this, row, rowIndex, e);
6731         }
6732         
6733         
6734     },
6735         
6736     onDblClick : function(e,el)
6737     {
6738         var cell = Roo.get(el);
6739         
6740         if(!cell || (!this.cellSelection && !this.rowSelection)){
6741             return;
6742         }
6743         
6744         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6745             cell = cell.findParent('td', false, true);
6746         }
6747         
6748         if(!cell || typeof(cell) == 'undefined'){
6749             return;
6750         }
6751         
6752         var row = cell.findParent('tr', false, true);
6753         
6754         if(!row || typeof(row) == 'undefined'){
6755             return;
6756         }
6757         
6758         var cellIndex = cell.dom.cellIndex;
6759         var rowIndex = this.getRowIndex(row);
6760         
6761         if(this.cellSelection){
6762             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6763         }
6764         
6765         if(this.rowSelection){
6766             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6767         }
6768     },
6769     
6770     sort : function(e,el)
6771     {
6772         var col = Roo.get(el);
6773         
6774         if(!col.hasClass('sortable')){
6775             return;
6776         }
6777         
6778         var sort = col.attr('sort');
6779         var dir = 'ASC';
6780         
6781         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6782             dir = 'DESC';
6783         }
6784         
6785         this.store.sortInfo = {field : sort, direction : dir};
6786         
6787         if (this.footer) {
6788             Roo.log("calling footer first");
6789             this.footer.onClick('first');
6790         } else {
6791         
6792             this.store.load({ params : { start : 0 } });
6793         }
6794     },
6795     
6796     renderHeader : function()
6797     {
6798         var header = {
6799             tag: 'thead',
6800             cn : []
6801         };
6802         
6803         var cm = this.cm;
6804         this.totalWidth = 0;
6805         
6806         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6807             
6808             var config = cm.config[i];
6809             
6810             var c = {
6811                 tag: 'th',
6812                 cls : 'x-hcol-' + i,
6813                 style : '',
6814                 html: cm.getColumnHeader(i)
6815             };
6816             
6817             var hh = '';
6818             
6819             if(typeof(config.sortable) != 'undefined' && config.sortable){
6820                 c.cls = 'sortable';
6821                 c.html = '<i class="glyphicon"></i>' + c.html;
6822             }
6823             
6824             // could use BS4 hidden-..-down 
6825             
6826             if(typeof(config.lgHeader) != 'undefined'){
6827                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
6828             }
6829             
6830             if(typeof(config.mdHeader) != 'undefined'){
6831                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6832             }
6833             
6834             if(typeof(config.smHeader) != 'undefined'){
6835                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6836             }
6837             
6838             if(typeof(config.xsHeader) != 'undefined'){
6839                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6840             }
6841             
6842             if(hh.length){
6843                 c.html = hh;
6844             }
6845             
6846             if(typeof(config.tooltip) != 'undefined'){
6847                 c.tooltip = config.tooltip;
6848             }
6849             
6850             if(typeof(config.colspan) != 'undefined'){
6851                 c.colspan = config.colspan;
6852             }
6853             
6854             if(typeof(config.hidden) != 'undefined' && config.hidden){
6855                 c.style += ' display:none;';
6856             }
6857             
6858             if(typeof(config.dataIndex) != 'undefined'){
6859                 c.sort = config.dataIndex;
6860             }
6861             
6862            
6863             
6864             if(typeof(config.align) != 'undefined' && config.align.length){
6865                 c.style += ' text-align:' + config.align + ';';
6866             }
6867             
6868             if(typeof(config.width) != 'undefined'){
6869                 c.style += ' width:' + config.width + 'px;';
6870                 this.totalWidth += config.width;
6871             } else {
6872                 this.totalWidth += 100; // assume minimum of 100 per column?
6873             }
6874             
6875             if(typeof(config.cls) != 'undefined'){
6876                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6877             }
6878             
6879             ['xs','sm','md','lg'].map(function(size){
6880                 
6881                 if(typeof(config[size]) == 'undefined'){
6882                     return;
6883                 }
6884                  
6885                 if (!config[size]) { // 0 = hidden
6886                     // BS 4 '0' is treated as hide that column and below.
6887                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
6888                     return;
6889                 }
6890                 
6891                 c.cls += ' col-' + size + '-' + config[size] + (
6892                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
6893                 );
6894                 
6895                 
6896             });
6897             
6898             header.cn.push(c)
6899         }
6900         
6901         return header;
6902     },
6903     
6904     renderBody : function()
6905     {
6906         var body = {
6907             tag: 'tbody',
6908             cn : [
6909                 {
6910                     tag: 'tr',
6911                     cn : [
6912                         {
6913                             tag : 'td',
6914                             colspan :  this.cm.getColumnCount()
6915                         }
6916                     ]
6917                 }
6918             ]
6919         };
6920         
6921         return body;
6922     },
6923     
6924     renderFooter : function()
6925     {
6926         var footer = {
6927             tag: 'tfoot',
6928             cn : [
6929                 {
6930                     tag: 'tr',
6931                     cn : [
6932                         {
6933                             tag : 'td',
6934                             colspan :  this.cm.getColumnCount()
6935                         }
6936                     ]
6937                 }
6938             ]
6939         };
6940         
6941         return footer;
6942     },
6943     
6944     
6945     
6946     onLoad : function()
6947     {
6948 //        Roo.log('ds onload');
6949         this.clear();
6950         
6951         var _this = this;
6952         var cm = this.cm;
6953         var ds = this.store;
6954         
6955         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6956             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6957             if (_this.store.sortInfo) {
6958                     
6959                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6960                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6961                 }
6962                 
6963                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6964                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6965                 }
6966             }
6967         });
6968         
6969         var tbody =  this.mainBody;
6970               
6971         if(ds.getCount() > 0){
6972             ds.data.each(function(d,rowIndex){
6973                 var row =  this.renderRow(cm, ds, rowIndex);
6974                 
6975                 tbody.createChild(row);
6976                 
6977                 var _this = this;
6978                 
6979                 if(row.cellObjects.length){
6980                     Roo.each(row.cellObjects, function(r){
6981                         _this.renderCellObject(r);
6982                     })
6983                 }
6984                 
6985             }, this);
6986         }
6987         
6988         var tfoot = this.el.select('tfoot', true).first();
6989         
6990         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6991             
6992             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6993             
6994             var total = this.ds.getTotalCount();
6995             
6996             if(this.footer.pageSize < total){
6997                 this.mainFoot.show();
6998             }
6999         }
7000         
7001         Roo.each(this.el.select('tbody td', true).elements, function(e){
7002             e.on('mouseover', _this.onMouseover, _this);
7003         });
7004         
7005         Roo.each(this.el.select('tbody td', true).elements, function(e){
7006             e.on('mouseout', _this.onMouseout, _this);
7007         });
7008         this.fireEvent('rowsrendered', this);
7009         
7010         this.autoSize();
7011     },
7012     
7013     
7014     onUpdate : function(ds,record)
7015     {
7016         this.refreshRow(record);
7017         this.autoSize();
7018     },
7019     
7020     onRemove : function(ds, record, index, isUpdate){
7021         if(isUpdate !== true){
7022             this.fireEvent("beforerowremoved", this, index, record);
7023         }
7024         var bt = this.mainBody.dom;
7025         
7026         var rows = this.el.select('tbody > tr', true).elements;
7027         
7028         if(typeof(rows[index]) != 'undefined'){
7029             bt.removeChild(rows[index].dom);
7030         }
7031         
7032 //        if(bt.rows[index]){
7033 //            bt.removeChild(bt.rows[index]);
7034 //        }
7035         
7036         if(isUpdate !== true){
7037             //this.stripeRows(index);
7038             //this.syncRowHeights(index, index);
7039             //this.layout();
7040             this.fireEvent("rowremoved", this, index, record);
7041         }
7042     },
7043     
7044     onAdd : function(ds, records, rowIndex)
7045     {
7046         //Roo.log('on Add called');
7047         // - note this does not handle multiple adding very well..
7048         var bt = this.mainBody.dom;
7049         for (var i =0 ; i < records.length;i++) {
7050             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7051             //Roo.log(records[i]);
7052             //Roo.log(this.store.getAt(rowIndex+i));
7053             this.insertRow(this.store, rowIndex + i, false);
7054             return;
7055         }
7056         
7057     },
7058     
7059     
7060     refreshRow : function(record){
7061         var ds = this.store, index;
7062         if(typeof record == 'number'){
7063             index = record;
7064             record = ds.getAt(index);
7065         }else{
7066             index = ds.indexOf(record);
7067         }
7068         this.insertRow(ds, index, true);
7069         this.autoSize();
7070         this.onRemove(ds, record, index+1, true);
7071         this.autoSize();
7072         //this.syncRowHeights(index, index);
7073         //this.layout();
7074         this.fireEvent("rowupdated", this, index, record);
7075     },
7076     
7077     insertRow : function(dm, rowIndex, isUpdate){
7078         
7079         if(!isUpdate){
7080             this.fireEvent("beforerowsinserted", this, rowIndex);
7081         }
7082             //var s = this.getScrollState();
7083         var row = this.renderRow(this.cm, this.store, rowIndex);
7084         // insert before rowIndex..
7085         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7086         
7087         var _this = this;
7088                 
7089         if(row.cellObjects.length){
7090             Roo.each(row.cellObjects, function(r){
7091                 _this.renderCellObject(r);
7092             })
7093         }
7094             
7095         if(!isUpdate){
7096             this.fireEvent("rowsinserted", this, rowIndex);
7097             //this.syncRowHeights(firstRow, lastRow);
7098             //this.stripeRows(firstRow);
7099             //this.layout();
7100         }
7101         
7102     },
7103     
7104     
7105     getRowDom : function(rowIndex)
7106     {
7107         var rows = this.el.select('tbody > tr', true).elements;
7108         
7109         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7110         
7111     },
7112     // returns the object tree for a tr..
7113   
7114     
7115     renderRow : function(cm, ds, rowIndex) 
7116     {
7117         var d = ds.getAt(rowIndex);
7118         
7119         var row = {
7120             tag : 'tr',
7121             cls : 'x-row-' + rowIndex,
7122             cn : []
7123         };
7124             
7125         var cellObjects = [];
7126         
7127         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7128             var config = cm.config[i];
7129             
7130             var renderer = cm.getRenderer(i);
7131             var value = '';
7132             var id = false;
7133             
7134             if(typeof(renderer) !== 'undefined'){
7135                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7136             }
7137             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7138             // and are rendered into the cells after the row is rendered - using the id for the element.
7139             
7140             if(typeof(value) === 'object'){
7141                 id = Roo.id();
7142                 cellObjects.push({
7143                     container : id,
7144                     cfg : value 
7145                 })
7146             }
7147             
7148             var rowcfg = {
7149                 record: d,
7150                 rowIndex : rowIndex,
7151                 colIndex : i,
7152                 rowClass : ''
7153             };
7154
7155             this.fireEvent('rowclass', this, rowcfg);
7156             
7157             var td = {
7158                 tag: 'td',
7159                 cls : rowcfg.rowClass + ' x-col-' + i,
7160                 style: '',
7161                 html: (typeof(value) === 'object') ? '' : value
7162             };
7163             
7164             if (id) {
7165                 td.id = id;
7166             }
7167             
7168             if(typeof(config.colspan) != 'undefined'){
7169                 td.colspan = config.colspan;
7170             }
7171             
7172             if(typeof(config.hidden) != 'undefined' && config.hidden){
7173                 td.style += ' display:none;';
7174             }
7175             
7176             if(typeof(config.align) != 'undefined' && config.align.length){
7177                 td.style += ' text-align:' + config.align + ';';
7178             }
7179             if(typeof(config.valign) != 'undefined' && config.valign.length){
7180                 td.style += ' vertical-align:' + config.valign + ';';
7181             }
7182             
7183             if(typeof(config.width) != 'undefined'){
7184                 td.style += ' width:' +  config.width + 'px;';
7185             }
7186             
7187             if(typeof(config.cursor) != 'undefined'){
7188                 td.style += ' cursor:' +  config.cursor + ';';
7189             }
7190             
7191             if(typeof(config.cls) != 'undefined'){
7192                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7193             }
7194             
7195             ['xs','sm','md','lg'].map(function(size){
7196                 
7197                 if(typeof(config[size]) == 'undefined'){
7198                     return;
7199                 }
7200                 
7201                 
7202                   
7203                 if (!config[size]) { // 0 = hidden
7204                     // BS 4 '0' is treated as hide that column and below.
7205                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
7206                     return;
7207                 }
7208                 
7209                 td.cls += ' col-' + size + '-' + config[size] + (
7210                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
7211                 );
7212                  
7213
7214             });
7215             
7216             row.cn.push(td);
7217            
7218         }
7219         
7220         row.cellObjects = cellObjects;
7221         
7222         return row;
7223           
7224     },
7225     
7226     
7227     
7228     onBeforeLoad : function()
7229     {
7230         
7231     },
7232      /**
7233      * Remove all rows
7234      */
7235     clear : function()
7236     {
7237         this.el.select('tbody', true).first().dom.innerHTML = '';
7238     },
7239     /**
7240      * Show or hide a row.
7241      * @param {Number} rowIndex to show or hide
7242      * @param {Boolean} state hide
7243      */
7244     setRowVisibility : function(rowIndex, state)
7245     {
7246         var bt = this.mainBody.dom;
7247         
7248         var rows = this.el.select('tbody > tr', true).elements;
7249         
7250         if(typeof(rows[rowIndex]) == 'undefined'){
7251             return;
7252         }
7253         rows[rowIndex].dom.style.display = state ? '' : 'none';
7254     },
7255     
7256     
7257     getSelectionModel : function(){
7258         if(!this.selModel){
7259             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7260         }
7261         return this.selModel;
7262     },
7263     /*
7264      * Render the Roo.bootstrap object from renderder
7265      */
7266     renderCellObject : function(r)
7267     {
7268         var _this = this;
7269         
7270         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7271         
7272         var t = r.cfg.render(r.container);
7273         
7274         if(r.cfg.cn){
7275             Roo.each(r.cfg.cn, function(c){
7276                 var child = {
7277                     container: t.getChildContainer(),
7278                     cfg: c
7279                 };
7280                 _this.renderCellObject(child);
7281             })
7282         }
7283     },
7284     
7285     getRowIndex : function(row)
7286     {
7287         var rowIndex = -1;
7288         
7289         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7290             if(el != row){
7291                 return;
7292             }
7293             
7294             rowIndex = index;
7295         });
7296         
7297         return rowIndex;
7298     },
7299      /**
7300      * Returns the grid's underlying element = used by panel.Grid
7301      * @return {Element} The element
7302      */
7303     getGridEl : function(){
7304         return this.el;
7305     },
7306      /**
7307      * Forces a resize - used by panel.Grid
7308      * @return {Element} The element
7309      */
7310     autoSize : function()
7311     {
7312         //var ctr = Roo.get(this.container.dom.parentElement);
7313         var ctr = Roo.get(this.el.dom);
7314         
7315         var thd = this.getGridEl().select('thead',true).first();
7316         var tbd = this.getGridEl().select('tbody', true).first();
7317         var tfd = this.getGridEl().select('tfoot', true).first();
7318         
7319         var cw = ctr.getWidth();
7320         
7321         if (tbd) {
7322             
7323             tbd.setSize(ctr.getWidth(),
7324                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7325             );
7326             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7327             cw -= barsize;
7328         }
7329         cw = Math.max(cw, this.totalWidth);
7330         this.getGridEl().select('tr',true).setWidth(cw);
7331         // resize 'expandable coloumn?
7332         
7333         return; // we doe not have a view in this design..
7334         
7335     },
7336     onBodyScroll: function()
7337     {
7338         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7339         if(this.mainHead){
7340             this.mainHead.setStyle({
7341                 'position' : 'relative',
7342                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7343             });
7344         }
7345         
7346         if(this.lazyLoad){
7347             
7348             var scrollHeight = this.mainBody.dom.scrollHeight;
7349             
7350             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7351             
7352             var height = this.mainBody.getHeight();
7353             
7354             if(scrollHeight - height == scrollTop) {
7355                 
7356                 var total = this.ds.getTotalCount();
7357                 
7358                 if(this.footer.cursor + this.footer.pageSize < total){
7359                     
7360                     this.footer.ds.load({
7361                         params : {
7362                             start : this.footer.cursor + this.footer.pageSize,
7363                             limit : this.footer.pageSize
7364                         },
7365                         add : true
7366                     });
7367                 }
7368             }
7369             
7370         }
7371     },
7372     
7373     onHeaderChange : function()
7374     {
7375         var header = this.renderHeader();
7376         var table = this.el.select('table', true).first();
7377         
7378         this.mainHead.remove();
7379         this.mainHead = table.createChild(header, this.mainBody, false);
7380     },
7381     
7382     onHiddenChange : function(colModel, colIndex, hidden)
7383     {
7384         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7385         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7386         
7387         this.CSS.updateRule(thSelector, "display", "");
7388         this.CSS.updateRule(tdSelector, "display", "");
7389         
7390         if(hidden){
7391             this.CSS.updateRule(thSelector, "display", "none");
7392             this.CSS.updateRule(tdSelector, "display", "none");
7393         }
7394         
7395         this.onHeaderChange();
7396         this.onLoad();
7397     },
7398     
7399     setColumnWidth: function(col_index, width)
7400     {
7401         // width = "md-2 xs-2..."
7402         if(!this.colModel.config[col_index]) {
7403             return;
7404         }
7405         
7406         var w = width.split(" ");
7407         
7408         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7409         
7410         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7411         
7412         
7413         for(var j = 0; j < w.length; j++) {
7414             
7415             if(!w[j]) {
7416                 continue;
7417             }
7418             
7419             var size_cls = w[j].split("-");
7420             
7421             if(!Number.isInteger(size_cls[1] * 1)) {
7422                 continue;
7423             }
7424             
7425             if(!this.colModel.config[col_index][size_cls[0]]) {
7426                 continue;
7427             }
7428             
7429             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7430                 continue;
7431             }
7432             
7433             h_row[0].classList.replace(
7434                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7435                 "col-"+size_cls[0]+"-"+size_cls[1]
7436             );
7437             
7438             for(var i = 0; i < rows.length; i++) {
7439                 
7440                 var size_cls = w[j].split("-");
7441                 
7442                 if(!Number.isInteger(size_cls[1] * 1)) {
7443                     continue;
7444                 }
7445                 
7446                 if(!this.colModel.config[col_index][size_cls[0]]) {
7447                     continue;
7448                 }
7449                 
7450                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7451                     continue;
7452                 }
7453                 
7454                 rows[i].classList.replace(
7455                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7456                     "col-"+size_cls[0]+"-"+size_cls[1]
7457                 );
7458             }
7459             
7460             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7461         }
7462     }
7463 });
7464
7465  
7466
7467  /*
7468  * - LGPL
7469  *
7470  * table cell
7471  * 
7472  */
7473
7474 /**
7475  * @class Roo.bootstrap.TableCell
7476  * @extends Roo.bootstrap.Component
7477  * Bootstrap TableCell class
7478  * @cfg {String} html cell contain text
7479  * @cfg {String} cls cell class
7480  * @cfg {String} tag cell tag (td|th) default td
7481  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7482  * @cfg {String} align Aligns the content in a cell
7483  * @cfg {String} axis Categorizes cells
7484  * @cfg {String} bgcolor Specifies the background color of a cell
7485  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7486  * @cfg {Number} colspan Specifies the number of columns a cell should span
7487  * @cfg {String} headers Specifies one or more header cells a cell is related to
7488  * @cfg {Number} height Sets the height of a cell
7489  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7490  * @cfg {Number} rowspan Sets the number of rows a cell should span
7491  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7492  * @cfg {String} valign Vertical aligns the content in a cell
7493  * @cfg {Number} width Specifies the width of a cell
7494  * 
7495  * @constructor
7496  * Create a new TableCell
7497  * @param {Object} config The config object
7498  */
7499
7500 Roo.bootstrap.TableCell = function(config){
7501     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7502 };
7503
7504 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7505     
7506     html: false,
7507     cls: false,
7508     tag: false,
7509     abbr: false,
7510     align: false,
7511     axis: false,
7512     bgcolor: false,
7513     charoff: false,
7514     colspan: false,
7515     headers: false,
7516     height: false,
7517     nowrap: false,
7518     rowspan: false,
7519     scope: false,
7520     valign: false,
7521     width: false,
7522     
7523     
7524     getAutoCreate : function(){
7525         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7526         
7527         cfg = {
7528             tag: 'td'
7529         };
7530         
7531         if(this.tag){
7532             cfg.tag = this.tag;
7533         }
7534         
7535         if (this.html) {
7536             cfg.html=this.html
7537         }
7538         if (this.cls) {
7539             cfg.cls=this.cls
7540         }
7541         if (this.abbr) {
7542             cfg.abbr=this.abbr
7543         }
7544         if (this.align) {
7545             cfg.align=this.align
7546         }
7547         if (this.axis) {
7548             cfg.axis=this.axis
7549         }
7550         if (this.bgcolor) {
7551             cfg.bgcolor=this.bgcolor
7552         }
7553         if (this.charoff) {
7554             cfg.charoff=this.charoff
7555         }
7556         if (this.colspan) {
7557             cfg.colspan=this.colspan
7558         }
7559         if (this.headers) {
7560             cfg.headers=this.headers
7561         }
7562         if (this.height) {
7563             cfg.height=this.height
7564         }
7565         if (this.nowrap) {
7566             cfg.nowrap=this.nowrap
7567         }
7568         if (this.rowspan) {
7569             cfg.rowspan=this.rowspan
7570         }
7571         if (this.scope) {
7572             cfg.scope=this.scope
7573         }
7574         if (this.valign) {
7575             cfg.valign=this.valign
7576         }
7577         if (this.width) {
7578             cfg.width=this.width
7579         }
7580         
7581         
7582         return cfg;
7583     }
7584    
7585 });
7586
7587  
7588
7589  /*
7590  * - LGPL
7591  *
7592  * table row
7593  * 
7594  */
7595
7596 /**
7597  * @class Roo.bootstrap.TableRow
7598  * @extends Roo.bootstrap.Component
7599  * Bootstrap TableRow class
7600  * @cfg {String} cls row class
7601  * @cfg {String} align Aligns the content in a table row
7602  * @cfg {String} bgcolor Specifies a background color for a table row
7603  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7604  * @cfg {String} valign Vertical aligns the content in a table row
7605  * 
7606  * @constructor
7607  * Create a new TableRow
7608  * @param {Object} config The config object
7609  */
7610
7611 Roo.bootstrap.TableRow = function(config){
7612     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7613 };
7614
7615 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7616     
7617     cls: false,
7618     align: false,
7619     bgcolor: false,
7620     charoff: false,
7621     valign: false,
7622     
7623     getAutoCreate : function(){
7624         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7625         
7626         cfg = {
7627             tag: 'tr'
7628         };
7629             
7630         if(this.cls){
7631             cfg.cls = this.cls;
7632         }
7633         if(this.align){
7634             cfg.align = this.align;
7635         }
7636         if(this.bgcolor){
7637             cfg.bgcolor = this.bgcolor;
7638         }
7639         if(this.charoff){
7640             cfg.charoff = this.charoff;
7641         }
7642         if(this.valign){
7643             cfg.valign = this.valign;
7644         }
7645         
7646         return cfg;
7647     }
7648    
7649 });
7650
7651  
7652
7653  /*
7654  * - LGPL
7655  *
7656  * table body
7657  * 
7658  */
7659
7660 /**
7661  * @class Roo.bootstrap.TableBody
7662  * @extends Roo.bootstrap.Component
7663  * Bootstrap TableBody class
7664  * @cfg {String} cls element class
7665  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7666  * @cfg {String} align Aligns the content inside the element
7667  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7668  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7669  * 
7670  * @constructor
7671  * Create a new TableBody
7672  * @param {Object} config The config object
7673  */
7674
7675 Roo.bootstrap.TableBody = function(config){
7676     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7677 };
7678
7679 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7680     
7681     cls: false,
7682     tag: false,
7683     align: false,
7684     charoff: false,
7685     valign: false,
7686     
7687     getAutoCreate : function(){
7688         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7689         
7690         cfg = {
7691             tag: 'tbody'
7692         };
7693             
7694         if (this.cls) {
7695             cfg.cls=this.cls
7696         }
7697         if(this.tag){
7698             cfg.tag = this.tag;
7699         }
7700         
7701         if(this.align){
7702             cfg.align = this.align;
7703         }
7704         if(this.charoff){
7705             cfg.charoff = this.charoff;
7706         }
7707         if(this.valign){
7708             cfg.valign = this.valign;
7709         }
7710         
7711         return cfg;
7712     }
7713     
7714     
7715 //    initEvents : function()
7716 //    {
7717 //        
7718 //        if(!this.store){
7719 //            return;
7720 //        }
7721 //        
7722 //        this.store = Roo.factory(this.store, Roo.data);
7723 //        this.store.on('load', this.onLoad, this);
7724 //        
7725 //        this.store.load();
7726 //        
7727 //    },
7728 //    
7729 //    onLoad: function () 
7730 //    {   
7731 //        this.fireEvent('load', this);
7732 //    }
7733 //    
7734 //   
7735 });
7736
7737  
7738
7739  /*
7740  * Based on:
7741  * Ext JS Library 1.1.1
7742  * Copyright(c) 2006-2007, Ext JS, LLC.
7743  *
7744  * Originally Released Under LGPL - original licence link has changed is not relivant.
7745  *
7746  * Fork - LGPL
7747  * <script type="text/javascript">
7748  */
7749
7750 // as we use this in bootstrap.
7751 Roo.namespace('Roo.form');
7752  /**
7753  * @class Roo.form.Action
7754  * Internal Class used to handle form actions
7755  * @constructor
7756  * @param {Roo.form.BasicForm} el The form element or its id
7757  * @param {Object} config Configuration options
7758  */
7759
7760  
7761  
7762 // define the action interface
7763 Roo.form.Action = function(form, options){
7764     this.form = form;
7765     this.options = options || {};
7766 };
7767 /**
7768  * Client Validation Failed
7769  * @const 
7770  */
7771 Roo.form.Action.CLIENT_INVALID = 'client';
7772 /**
7773  * Server Validation Failed
7774  * @const 
7775  */
7776 Roo.form.Action.SERVER_INVALID = 'server';
7777  /**
7778  * Connect to Server Failed
7779  * @const 
7780  */
7781 Roo.form.Action.CONNECT_FAILURE = 'connect';
7782 /**
7783  * Reading Data from Server Failed
7784  * @const 
7785  */
7786 Roo.form.Action.LOAD_FAILURE = 'load';
7787
7788 Roo.form.Action.prototype = {
7789     type : 'default',
7790     failureType : undefined,
7791     response : undefined,
7792     result : undefined,
7793
7794     // interface method
7795     run : function(options){
7796
7797     },
7798
7799     // interface method
7800     success : function(response){
7801
7802     },
7803
7804     // interface method
7805     handleResponse : function(response){
7806
7807     },
7808
7809     // default connection failure
7810     failure : function(response){
7811         
7812         this.response = response;
7813         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7814         this.form.afterAction(this, false);
7815     },
7816
7817     processResponse : function(response){
7818         this.response = response;
7819         if(!response.responseText){
7820             return true;
7821         }
7822         this.result = this.handleResponse(response);
7823         return this.result;
7824     },
7825
7826     // utility functions used internally
7827     getUrl : function(appendParams){
7828         var url = this.options.url || this.form.url || this.form.el.dom.action;
7829         if(appendParams){
7830             var p = this.getParams();
7831             if(p){
7832                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7833             }
7834         }
7835         return url;
7836     },
7837
7838     getMethod : function(){
7839         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7840     },
7841
7842     getParams : function(){
7843         var bp = this.form.baseParams;
7844         var p = this.options.params;
7845         if(p){
7846             if(typeof p == "object"){
7847                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7848             }else if(typeof p == 'string' && bp){
7849                 p += '&' + Roo.urlEncode(bp);
7850             }
7851         }else if(bp){
7852             p = Roo.urlEncode(bp);
7853         }
7854         return p;
7855     },
7856
7857     createCallback : function(){
7858         return {
7859             success: this.success,
7860             failure: this.failure,
7861             scope: this,
7862             timeout: (this.form.timeout*1000),
7863             upload: this.form.fileUpload ? this.success : undefined
7864         };
7865     }
7866 };
7867
7868 Roo.form.Action.Submit = function(form, options){
7869     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7870 };
7871
7872 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7873     type : 'submit',
7874
7875     haveProgress : false,
7876     uploadComplete : false,
7877     
7878     // uploadProgress indicator.
7879     uploadProgress : function()
7880     {
7881         if (!this.form.progressUrl) {
7882             return;
7883         }
7884         
7885         if (!this.haveProgress) {
7886             Roo.MessageBox.progress("Uploading", "Uploading");
7887         }
7888         if (this.uploadComplete) {
7889            Roo.MessageBox.hide();
7890            return;
7891         }
7892         
7893         this.haveProgress = true;
7894    
7895         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7896         
7897         var c = new Roo.data.Connection();
7898         c.request({
7899             url : this.form.progressUrl,
7900             params: {
7901                 id : uid
7902             },
7903             method: 'GET',
7904             success : function(req){
7905                //console.log(data);
7906                 var rdata = false;
7907                 var edata;
7908                 try  {
7909                    rdata = Roo.decode(req.responseText)
7910                 } catch (e) {
7911                     Roo.log("Invalid data from server..");
7912                     Roo.log(edata);
7913                     return;
7914                 }
7915                 if (!rdata || !rdata.success) {
7916                     Roo.log(rdata);
7917                     Roo.MessageBox.alert(Roo.encode(rdata));
7918                     return;
7919                 }
7920                 var data = rdata.data;
7921                 
7922                 if (this.uploadComplete) {
7923                    Roo.MessageBox.hide();
7924                    return;
7925                 }
7926                    
7927                 if (data){
7928                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7929                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7930                     );
7931                 }
7932                 this.uploadProgress.defer(2000,this);
7933             },
7934        
7935             failure: function(data) {
7936                 Roo.log('progress url failed ');
7937                 Roo.log(data);
7938             },
7939             scope : this
7940         });
7941            
7942     },
7943     
7944     
7945     run : function()
7946     {
7947         // run get Values on the form, so it syncs any secondary forms.
7948         this.form.getValues();
7949         
7950         var o = this.options;
7951         var method = this.getMethod();
7952         var isPost = method == 'POST';
7953         if(o.clientValidation === false || this.form.isValid()){
7954             
7955             if (this.form.progressUrl) {
7956                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7957                     (new Date() * 1) + '' + Math.random());
7958                     
7959             } 
7960             
7961             
7962             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7963                 form:this.form.el.dom,
7964                 url:this.getUrl(!isPost),
7965                 method: method,
7966                 params:isPost ? this.getParams() : null,
7967                 isUpload: this.form.fileUpload,
7968                 formData : this.form.formData
7969             }));
7970             
7971             this.uploadProgress();
7972
7973         }else if (o.clientValidation !== false){ // client validation failed
7974             this.failureType = Roo.form.Action.CLIENT_INVALID;
7975             this.form.afterAction(this, false);
7976         }
7977     },
7978
7979     success : function(response)
7980     {
7981         this.uploadComplete= true;
7982         if (this.haveProgress) {
7983             Roo.MessageBox.hide();
7984         }
7985         
7986         
7987         var result = this.processResponse(response);
7988         if(result === true || result.success){
7989             this.form.afterAction(this, true);
7990             return;
7991         }
7992         if(result.errors){
7993             this.form.markInvalid(result.errors);
7994             this.failureType = Roo.form.Action.SERVER_INVALID;
7995         }
7996         this.form.afterAction(this, false);
7997     },
7998     failure : function(response)
7999     {
8000         this.uploadComplete= true;
8001         if (this.haveProgress) {
8002             Roo.MessageBox.hide();
8003         }
8004         
8005         this.response = response;
8006         this.failureType = Roo.form.Action.CONNECT_FAILURE;
8007         this.form.afterAction(this, false);
8008     },
8009     
8010     handleResponse : function(response){
8011         if(this.form.errorReader){
8012             var rs = this.form.errorReader.read(response);
8013             var errors = [];
8014             if(rs.records){
8015                 for(var i = 0, len = rs.records.length; i < len; i++) {
8016                     var r = rs.records[i];
8017                     errors[i] = r.data;
8018                 }
8019             }
8020             if(errors.length < 1){
8021                 errors = null;
8022             }
8023             return {
8024                 success : rs.success,
8025                 errors : errors
8026             };
8027         }
8028         var ret = false;
8029         try {
8030             ret = Roo.decode(response.responseText);
8031         } catch (e) {
8032             ret = {
8033                 success: false,
8034                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8035                 errors : []
8036             };
8037         }
8038         return ret;
8039         
8040     }
8041 });
8042
8043
8044 Roo.form.Action.Load = function(form, options){
8045     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8046     this.reader = this.form.reader;
8047 };
8048
8049 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8050     type : 'load',
8051
8052     run : function(){
8053         
8054         Roo.Ajax.request(Roo.apply(
8055                 this.createCallback(), {
8056                     method:this.getMethod(),
8057                     url:this.getUrl(false),
8058                     params:this.getParams()
8059         }));
8060     },
8061
8062     success : function(response){
8063         
8064         var result = this.processResponse(response);
8065         if(result === true || !result.success || !result.data){
8066             this.failureType = Roo.form.Action.LOAD_FAILURE;
8067             this.form.afterAction(this, false);
8068             return;
8069         }
8070         this.form.clearInvalid();
8071         this.form.setValues(result.data);
8072         this.form.afterAction(this, true);
8073     },
8074
8075     handleResponse : function(response){
8076         if(this.form.reader){
8077             var rs = this.form.reader.read(response);
8078             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8079             return {
8080                 success : rs.success,
8081                 data : data
8082             };
8083         }
8084         return Roo.decode(response.responseText);
8085     }
8086 });
8087
8088 Roo.form.Action.ACTION_TYPES = {
8089     'load' : Roo.form.Action.Load,
8090     'submit' : Roo.form.Action.Submit
8091 };/*
8092  * - LGPL
8093  *
8094  * form
8095  *
8096  */
8097
8098 /**
8099  * @class Roo.bootstrap.Form
8100  * @extends Roo.bootstrap.Component
8101  * Bootstrap Form class
8102  * @cfg {String} method  GET | POST (default POST)
8103  * @cfg {String} labelAlign top | left (default top)
8104  * @cfg {String} align left  | right - for navbars
8105  * @cfg {Boolean} loadMask load mask when submit (default true)
8106
8107  *
8108  * @constructor
8109  * Create a new Form
8110  * @param {Object} config The config object
8111  */
8112
8113
8114 Roo.bootstrap.Form = function(config){
8115     
8116     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8117     
8118     Roo.bootstrap.Form.popover.apply();
8119     
8120     this.addEvents({
8121         /**
8122          * @event clientvalidation
8123          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8124          * @param {Form} this
8125          * @param {Boolean} valid true if the form has passed client-side validation
8126          */
8127         clientvalidation: true,
8128         /**
8129          * @event beforeaction
8130          * Fires before any action is performed. Return false to cancel the action.
8131          * @param {Form} this
8132          * @param {Action} action The action to be performed
8133          */
8134         beforeaction: true,
8135         /**
8136          * @event actionfailed
8137          * Fires when an action fails.
8138          * @param {Form} this
8139          * @param {Action} action The action that failed
8140          */
8141         actionfailed : true,
8142         /**
8143          * @event actioncomplete
8144          * Fires when an action is completed.
8145          * @param {Form} this
8146          * @param {Action} action The action that completed
8147          */
8148         actioncomplete : true
8149     });
8150 };
8151
8152 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8153
8154      /**
8155      * @cfg {String} method
8156      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8157      */
8158     method : 'POST',
8159     /**
8160      * @cfg {String} url
8161      * The URL to use for form actions if one isn't supplied in the action options.
8162      */
8163     /**
8164      * @cfg {Boolean} fileUpload
8165      * Set to true if this form is a file upload.
8166      */
8167
8168     /**
8169      * @cfg {Object} baseParams
8170      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8171      */
8172
8173     /**
8174      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8175      */
8176     timeout: 30,
8177     /**
8178      * @cfg {Sting} align (left|right) for navbar forms
8179      */
8180     align : 'left',
8181
8182     // private
8183     activeAction : null,
8184
8185     /**
8186      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8187      * element by passing it or its id or mask the form itself by passing in true.
8188      * @type Mixed
8189      */
8190     waitMsgTarget : false,
8191
8192     loadMask : true,
8193     
8194     /**
8195      * @cfg {Boolean} errorMask (true|false) default false
8196      */
8197     errorMask : false,
8198     
8199     /**
8200      * @cfg {Number} maskOffset Default 100
8201      */
8202     maskOffset : 100,
8203     
8204     /**
8205      * @cfg {Boolean} maskBody
8206      */
8207     maskBody : false,
8208
8209     getAutoCreate : function(){
8210
8211         var cfg = {
8212             tag: 'form',
8213             method : this.method || 'POST',
8214             id : this.id || Roo.id(),
8215             cls : ''
8216         };
8217         if (this.parent().xtype.match(/^Nav/)) {
8218             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8219
8220         }
8221
8222         if (this.labelAlign == 'left' ) {
8223             cfg.cls += ' form-horizontal';
8224         }
8225
8226
8227         return cfg;
8228     },
8229     initEvents : function()
8230     {
8231         this.el.on('submit', this.onSubmit, this);
8232         // this was added as random key presses on the form where triggering form submit.
8233         this.el.on('keypress', function(e) {
8234             if (e.getCharCode() != 13) {
8235                 return true;
8236             }
8237             // we might need to allow it for textareas.. and some other items.
8238             // check e.getTarget().
8239
8240             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8241                 return true;
8242             }
8243
8244             Roo.log("keypress blocked");
8245
8246             e.preventDefault();
8247             return false;
8248         });
8249         
8250     },
8251     // private
8252     onSubmit : function(e){
8253         e.stopEvent();
8254     },
8255
8256      /**
8257      * Returns true if client-side validation on the form is successful.
8258      * @return Boolean
8259      */
8260     isValid : function(){
8261         var items = this.getItems();
8262         var valid = true;
8263         var target = false;
8264         
8265         items.each(function(f){
8266             
8267             if(f.validate()){
8268                 return;
8269             }
8270             
8271             Roo.log('invalid field: ' + f.name);
8272             
8273             valid = false;
8274
8275             if(!target && f.el.isVisible(true)){
8276                 target = f;
8277             }
8278            
8279         });
8280         
8281         if(this.errorMask && !valid){
8282             Roo.bootstrap.Form.popover.mask(this, target);
8283         }
8284         
8285         return valid;
8286     },
8287     
8288     /**
8289      * Returns true if any fields in this form have changed since their original load.
8290      * @return Boolean
8291      */
8292     isDirty : function(){
8293         var dirty = false;
8294         var items = this.getItems();
8295         items.each(function(f){
8296            if(f.isDirty()){
8297                dirty = true;
8298                return false;
8299            }
8300            return true;
8301         });
8302         return dirty;
8303     },
8304      /**
8305      * Performs a predefined action (submit or load) or custom actions you define on this form.
8306      * @param {String} actionName The name of the action type
8307      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8308      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8309      * accept other config options):
8310      * <pre>
8311 Property          Type             Description
8312 ----------------  ---------------  ----------------------------------------------------------------------------------
8313 url               String           The url for the action (defaults to the form's url)
8314 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8315 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8316 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8317                                    validate the form on the client (defaults to false)
8318      * </pre>
8319      * @return {BasicForm} this
8320      */
8321     doAction : function(action, options){
8322         if(typeof action == 'string'){
8323             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8324         }
8325         if(this.fireEvent('beforeaction', this, action) !== false){
8326             this.beforeAction(action);
8327             action.run.defer(100, action);
8328         }
8329         return this;
8330     },
8331
8332     // private
8333     beforeAction : function(action){
8334         var o = action.options;
8335         
8336         if(this.loadMask){
8337             
8338             if(this.maskBody){
8339                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8340             } else {
8341                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8342             }
8343         }
8344         // not really supported yet.. ??
8345
8346         //if(this.waitMsgTarget === true){
8347         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8348         //}else if(this.waitMsgTarget){
8349         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8350         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8351         //}else {
8352         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8353        // }
8354
8355     },
8356
8357     // private
8358     afterAction : function(action, success){
8359         this.activeAction = null;
8360         var o = action.options;
8361
8362         if(this.loadMask){
8363             
8364             if(this.maskBody){
8365                 Roo.get(document.body).unmask();
8366             } else {
8367                 this.el.unmask();
8368             }
8369         }
8370         
8371         //if(this.waitMsgTarget === true){
8372 //            this.el.unmask();
8373         //}else if(this.waitMsgTarget){
8374         //    this.waitMsgTarget.unmask();
8375         //}else{
8376         //    Roo.MessageBox.updateProgress(1);
8377         //    Roo.MessageBox.hide();
8378        // }
8379         //
8380         if(success){
8381             if(o.reset){
8382                 this.reset();
8383             }
8384             Roo.callback(o.success, o.scope, [this, action]);
8385             this.fireEvent('actioncomplete', this, action);
8386
8387         }else{
8388
8389             // failure condition..
8390             // we have a scenario where updates need confirming.
8391             // eg. if a locking scenario exists..
8392             // we look for { errors : { needs_confirm : true }} in the response.
8393             if (
8394                 (typeof(action.result) != 'undefined')  &&
8395                 (typeof(action.result.errors) != 'undefined')  &&
8396                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8397            ){
8398                 var _t = this;
8399                 Roo.log("not supported yet");
8400                  /*
8401
8402                 Roo.MessageBox.confirm(
8403                     "Change requires confirmation",
8404                     action.result.errorMsg,
8405                     function(r) {
8406                         if (r != 'yes') {
8407                             return;
8408                         }
8409                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8410                     }
8411
8412                 );
8413                 */
8414
8415
8416                 return;
8417             }
8418
8419             Roo.callback(o.failure, o.scope, [this, action]);
8420             // show an error message if no failed handler is set..
8421             if (!this.hasListener('actionfailed')) {
8422                 Roo.log("need to add dialog support");
8423                 /*
8424                 Roo.MessageBox.alert("Error",
8425                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8426                         action.result.errorMsg :
8427                         "Saving Failed, please check your entries or try again"
8428                 );
8429                 */
8430             }
8431
8432             this.fireEvent('actionfailed', this, action);
8433         }
8434
8435     },
8436     /**
8437      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8438      * @param {String} id The value to search for
8439      * @return Field
8440      */
8441     findField : function(id){
8442         var items = this.getItems();
8443         var field = items.get(id);
8444         if(!field){
8445              items.each(function(f){
8446                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8447                     field = f;
8448                     return false;
8449                 }
8450                 return true;
8451             });
8452         }
8453         return field || null;
8454     },
8455      /**
8456      * Mark fields in this form invalid in bulk.
8457      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8458      * @return {BasicForm} this
8459      */
8460     markInvalid : function(errors){
8461         if(errors instanceof Array){
8462             for(var i = 0, len = errors.length; i < len; i++){
8463                 var fieldError = errors[i];
8464                 var f = this.findField(fieldError.id);
8465                 if(f){
8466                     f.markInvalid(fieldError.msg);
8467                 }
8468             }
8469         }else{
8470             var field, id;
8471             for(id in errors){
8472                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8473                     field.markInvalid(errors[id]);
8474                 }
8475             }
8476         }
8477         //Roo.each(this.childForms || [], function (f) {
8478         //    f.markInvalid(errors);
8479         //});
8480
8481         return this;
8482     },
8483
8484     /**
8485      * Set values for fields in this form in bulk.
8486      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8487      * @return {BasicForm} this
8488      */
8489     setValues : function(values){
8490         if(values instanceof Array){ // array of objects
8491             for(var i = 0, len = values.length; i < len; i++){
8492                 var v = values[i];
8493                 var f = this.findField(v.id);
8494                 if(f){
8495                     f.setValue(v.value);
8496                     if(this.trackResetOnLoad){
8497                         f.originalValue = f.getValue();
8498                     }
8499                 }
8500             }
8501         }else{ // object hash
8502             var field, id;
8503             for(id in values){
8504                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8505
8506                     if (field.setFromData &&
8507                         field.valueField &&
8508                         field.displayField &&
8509                         // combos' with local stores can
8510                         // be queried via setValue()
8511                         // to set their value..
8512                         (field.store && !field.store.isLocal)
8513                         ) {
8514                         // it's a combo
8515                         var sd = { };
8516                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8517                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8518                         field.setFromData(sd);
8519
8520                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8521                         
8522                         field.setFromData(values);
8523                         
8524                     } else {
8525                         field.setValue(values[id]);
8526                     }
8527
8528
8529                     if(this.trackResetOnLoad){
8530                         field.originalValue = field.getValue();
8531                     }
8532                 }
8533             }
8534         }
8535
8536         //Roo.each(this.childForms || [], function (f) {
8537         //    f.setValues(values);
8538         //});
8539
8540         return this;
8541     },
8542
8543     /**
8544      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8545      * they are returned as an array.
8546      * @param {Boolean} asString
8547      * @return {Object}
8548      */
8549     getValues : function(asString){
8550         //if (this.childForms) {
8551             // copy values from the child forms
8552         //    Roo.each(this.childForms, function (f) {
8553         //        this.setValues(f.getValues());
8554         //    }, this);
8555         //}
8556
8557
8558
8559         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8560         if(asString === true){
8561             return fs;
8562         }
8563         return Roo.urlDecode(fs);
8564     },
8565
8566     /**
8567      * Returns the fields in this form as an object with key/value pairs.
8568      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8569      * @return {Object}
8570      */
8571     getFieldValues : function(with_hidden)
8572     {
8573         var items = this.getItems();
8574         var ret = {};
8575         items.each(function(f){
8576             
8577             if (!f.getName()) {
8578                 return;
8579             }
8580             
8581             var v = f.getValue();
8582             
8583             if (f.inputType =='radio') {
8584                 if (typeof(ret[f.getName()]) == 'undefined') {
8585                     ret[f.getName()] = ''; // empty..
8586                 }
8587
8588                 if (!f.el.dom.checked) {
8589                     return;
8590
8591                 }
8592                 v = f.el.dom.value;
8593
8594             }
8595             
8596             if(f.xtype == 'MoneyField'){
8597                 ret[f.currencyName] = f.getCurrency();
8598             }
8599
8600             // not sure if this supported any more..
8601             if ((typeof(v) == 'object') && f.getRawValue) {
8602                 v = f.getRawValue() ; // dates..
8603             }
8604             // combo boxes where name != hiddenName...
8605             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8606                 ret[f.name] = f.getRawValue();
8607             }
8608             ret[f.getName()] = v;
8609         });
8610
8611         return ret;
8612     },
8613
8614     /**
8615      * Clears all invalid messages in this form.
8616      * @return {BasicForm} this
8617      */
8618     clearInvalid : function(){
8619         var items = this.getItems();
8620
8621         items.each(function(f){
8622            f.clearInvalid();
8623         });
8624
8625         return this;
8626     },
8627
8628     /**
8629      * Resets this form.
8630      * @return {BasicForm} this
8631      */
8632     reset : function(){
8633         var items = this.getItems();
8634         items.each(function(f){
8635             f.reset();
8636         });
8637
8638         Roo.each(this.childForms || [], function (f) {
8639             f.reset();
8640         });
8641
8642
8643         return this;
8644     },
8645     
8646     getItems : function()
8647     {
8648         var r=new Roo.util.MixedCollection(false, function(o){
8649             return o.id || (o.id = Roo.id());
8650         });
8651         var iter = function(el) {
8652             if (el.inputEl) {
8653                 r.add(el);
8654             }
8655             if (!el.items) {
8656                 return;
8657             }
8658             Roo.each(el.items,function(e) {
8659                 iter(e);
8660             });
8661         };
8662
8663         iter(this);
8664         return r;
8665     },
8666     
8667     hideFields : function(items)
8668     {
8669         Roo.each(items, function(i){
8670             
8671             var f = this.findField(i);
8672             
8673             if(!f){
8674                 return;
8675             }
8676             
8677             f.hide();
8678             
8679         }, this);
8680     },
8681     
8682     showFields : function(items)
8683     {
8684         Roo.each(items, function(i){
8685             
8686             var f = this.findField(i);
8687             
8688             if(!f){
8689                 return;
8690             }
8691             
8692             f.show();
8693             
8694         }, this);
8695     }
8696
8697 });
8698
8699 Roo.apply(Roo.bootstrap.Form, {
8700     
8701     popover : {
8702         
8703         padding : 5,
8704         
8705         isApplied : false,
8706         
8707         isMasked : false,
8708         
8709         form : false,
8710         
8711         target : false,
8712         
8713         toolTip : false,
8714         
8715         intervalID : false,
8716         
8717         maskEl : false,
8718         
8719         apply : function()
8720         {
8721             if(this.isApplied){
8722                 return;
8723             }
8724             
8725             this.maskEl = {
8726                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8727                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8728                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8729                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8730             };
8731             
8732             this.maskEl.top.enableDisplayMode("block");
8733             this.maskEl.left.enableDisplayMode("block");
8734             this.maskEl.bottom.enableDisplayMode("block");
8735             this.maskEl.right.enableDisplayMode("block");
8736             
8737             this.toolTip = new Roo.bootstrap.Tooltip({
8738                 cls : 'roo-form-error-popover',
8739                 alignment : {
8740                     'left' : ['r-l', [-2,0], 'right'],
8741                     'right' : ['l-r', [2,0], 'left'],
8742                     'bottom' : ['tl-bl', [0,2], 'top'],
8743                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8744                 }
8745             });
8746             
8747             this.toolTip.render(Roo.get(document.body));
8748
8749             this.toolTip.el.enableDisplayMode("block");
8750             
8751             Roo.get(document.body).on('click', function(){
8752                 this.unmask();
8753             }, this);
8754             
8755             Roo.get(document.body).on('touchstart', function(){
8756                 this.unmask();
8757             }, this);
8758             
8759             this.isApplied = true
8760         },
8761         
8762         mask : function(form, target)
8763         {
8764             this.form = form;
8765             
8766             this.target = target;
8767             
8768             if(!this.form.errorMask || !target.el){
8769                 return;
8770             }
8771             
8772             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8773             
8774             Roo.log(scrollable);
8775             
8776             var ot = this.target.el.calcOffsetsTo(scrollable);
8777             
8778             var scrollTo = ot[1] - this.form.maskOffset;
8779             
8780             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8781             
8782             scrollable.scrollTo('top', scrollTo);
8783             
8784             var box = this.target.el.getBox();
8785             Roo.log(box);
8786             var zIndex = Roo.bootstrap.Modal.zIndex++;
8787
8788             
8789             this.maskEl.top.setStyle('position', 'absolute');
8790             this.maskEl.top.setStyle('z-index', zIndex);
8791             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8792             this.maskEl.top.setLeft(0);
8793             this.maskEl.top.setTop(0);
8794             this.maskEl.top.show();
8795             
8796             this.maskEl.left.setStyle('position', 'absolute');
8797             this.maskEl.left.setStyle('z-index', zIndex);
8798             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8799             this.maskEl.left.setLeft(0);
8800             this.maskEl.left.setTop(box.y - this.padding);
8801             this.maskEl.left.show();
8802
8803             this.maskEl.bottom.setStyle('position', 'absolute');
8804             this.maskEl.bottom.setStyle('z-index', zIndex);
8805             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8806             this.maskEl.bottom.setLeft(0);
8807             this.maskEl.bottom.setTop(box.bottom + this.padding);
8808             this.maskEl.bottom.show();
8809
8810             this.maskEl.right.setStyle('position', 'absolute');
8811             this.maskEl.right.setStyle('z-index', zIndex);
8812             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8813             this.maskEl.right.setLeft(box.right + this.padding);
8814             this.maskEl.right.setTop(box.y - this.padding);
8815             this.maskEl.right.show();
8816
8817             this.toolTip.bindEl = this.target.el;
8818
8819             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8820
8821             var tip = this.target.blankText;
8822
8823             if(this.target.getValue() !== '' ) {
8824                 
8825                 if (this.target.invalidText.length) {
8826                     tip = this.target.invalidText;
8827                 } else if (this.target.regexText.length){
8828                     tip = this.target.regexText;
8829                 }
8830             }
8831
8832             this.toolTip.show(tip);
8833
8834             this.intervalID = window.setInterval(function() {
8835                 Roo.bootstrap.Form.popover.unmask();
8836             }, 10000);
8837
8838             window.onwheel = function(){ return false;};
8839             
8840             (function(){ this.isMasked = true; }).defer(500, this);
8841             
8842         },
8843         
8844         unmask : function()
8845         {
8846             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8847                 return;
8848             }
8849             
8850             this.maskEl.top.setStyle('position', 'absolute');
8851             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8852             this.maskEl.top.hide();
8853
8854             this.maskEl.left.setStyle('position', 'absolute');
8855             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8856             this.maskEl.left.hide();
8857
8858             this.maskEl.bottom.setStyle('position', 'absolute');
8859             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8860             this.maskEl.bottom.hide();
8861
8862             this.maskEl.right.setStyle('position', 'absolute');
8863             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8864             this.maskEl.right.hide();
8865             
8866             this.toolTip.hide();
8867             
8868             this.toolTip.el.hide();
8869             
8870             window.onwheel = function(){ return true;};
8871             
8872             if(this.intervalID){
8873                 window.clearInterval(this.intervalID);
8874                 this.intervalID = false;
8875             }
8876             
8877             this.isMasked = false;
8878             
8879         }
8880         
8881     }
8882     
8883 });
8884
8885 /*
8886  * Based on:
8887  * Ext JS Library 1.1.1
8888  * Copyright(c) 2006-2007, Ext JS, LLC.
8889  *
8890  * Originally Released Under LGPL - original licence link has changed is not relivant.
8891  *
8892  * Fork - LGPL
8893  * <script type="text/javascript">
8894  */
8895 /**
8896  * @class Roo.form.VTypes
8897  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8898  * @singleton
8899  */
8900 Roo.form.VTypes = function(){
8901     // closure these in so they are only created once.
8902     var alpha = /^[a-zA-Z_]+$/;
8903     var alphanum = /^[a-zA-Z0-9_]+$/;
8904     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8905     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8906
8907     // All these messages and functions are configurable
8908     return {
8909         /**
8910          * The function used to validate email addresses
8911          * @param {String} value The email address
8912          */
8913         'email' : function(v){
8914             return email.test(v);
8915         },
8916         /**
8917          * The error text to display when the email validation function returns false
8918          * @type String
8919          */
8920         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8921         /**
8922          * The keystroke filter mask to be applied on email input
8923          * @type RegExp
8924          */
8925         'emailMask' : /[a-z0-9_\.\-@]/i,
8926
8927         /**
8928          * The function used to validate URLs
8929          * @param {String} value The URL
8930          */
8931         'url' : function(v){
8932             return url.test(v);
8933         },
8934         /**
8935          * The error text to display when the url validation function returns false
8936          * @type String
8937          */
8938         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8939         
8940         /**
8941          * The function used to validate alpha values
8942          * @param {String} value The value
8943          */
8944         'alpha' : function(v){
8945             return alpha.test(v);
8946         },
8947         /**
8948          * The error text to display when the alpha validation function returns false
8949          * @type String
8950          */
8951         'alphaText' : 'This field should only contain letters and _',
8952         /**
8953          * The keystroke filter mask to be applied on alpha input
8954          * @type RegExp
8955          */
8956         'alphaMask' : /[a-z_]/i,
8957
8958         /**
8959          * The function used to validate alphanumeric values
8960          * @param {String} value The value
8961          */
8962         'alphanum' : function(v){
8963             return alphanum.test(v);
8964         },
8965         /**
8966          * The error text to display when the alphanumeric validation function returns false
8967          * @type String
8968          */
8969         'alphanumText' : 'This field should only contain letters, numbers and _',
8970         /**
8971          * The keystroke filter mask to be applied on alphanumeric input
8972          * @type RegExp
8973          */
8974         'alphanumMask' : /[a-z0-9_]/i
8975     };
8976 }();/*
8977  * - LGPL
8978  *
8979  * Input
8980  * 
8981  */
8982
8983 /**
8984  * @class Roo.bootstrap.Input
8985  * @extends Roo.bootstrap.Component
8986  * Bootstrap Input class
8987  * @cfg {Boolean} disabled is it disabled
8988  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8989  * @cfg {String} name name of the input
8990  * @cfg {string} fieldLabel - the label associated
8991  * @cfg {string} placeholder - placeholder to put in text.
8992  * @cfg {string}  before - input group add on before
8993  * @cfg {string} after - input group add on after
8994  * @cfg {string} size - (lg|sm) or leave empty..
8995  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8996  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8997  * @cfg {Number} md colspan out of 12 for computer-sized screens
8998  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8999  * @cfg {string} value default value of the input
9000  * @cfg {Number} labelWidth set the width of label 
9001  * @cfg {Number} labellg set the width of label (1-12)
9002  * @cfg {Number} labelmd set the width of label (1-12)
9003  * @cfg {Number} labelsm set the width of label (1-12)
9004  * @cfg {Number} labelxs set the width of label (1-12)
9005  * @cfg {String} labelAlign (top|left)
9006  * @cfg {Boolean} readOnly Specifies that the field should be read-only
9007  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
9008  * @cfg {String} indicatorpos (left|right) default left
9009  * @cfg {String} capture (user|camera) use for file input only. (default empty)
9010  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
9011
9012  * @cfg {String} align (left|center|right) Default left
9013  * @cfg {Boolean} forceFeedback (true|false) Default false
9014  * 
9015  * @constructor
9016  * Create a new Input
9017  * @param {Object} config The config object
9018  */
9019
9020 Roo.bootstrap.Input = function(config){
9021     
9022     Roo.bootstrap.Input.superclass.constructor.call(this, config);
9023     
9024     this.addEvents({
9025         /**
9026          * @event focus
9027          * Fires when this field receives input focus.
9028          * @param {Roo.form.Field} this
9029          */
9030         focus : true,
9031         /**
9032          * @event blur
9033          * Fires when this field loses input focus.
9034          * @param {Roo.form.Field} this
9035          */
9036         blur : true,
9037         /**
9038          * @event specialkey
9039          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9040          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9041          * @param {Roo.form.Field} this
9042          * @param {Roo.EventObject} e The event object
9043          */
9044         specialkey : true,
9045         /**
9046          * @event change
9047          * Fires just before the field blurs if the field value has changed.
9048          * @param {Roo.form.Field} this
9049          * @param {Mixed} newValue The new value
9050          * @param {Mixed} oldValue The original value
9051          */
9052         change : true,
9053         /**
9054          * @event invalid
9055          * Fires after the field has been marked as invalid.
9056          * @param {Roo.form.Field} this
9057          * @param {String} msg The validation message
9058          */
9059         invalid : true,
9060         /**
9061          * @event valid
9062          * Fires after the field has been validated with no errors.
9063          * @param {Roo.form.Field} this
9064          */
9065         valid : true,
9066          /**
9067          * @event keyup
9068          * Fires after the key up
9069          * @param {Roo.form.Field} this
9070          * @param {Roo.EventObject}  e The event Object
9071          */
9072         keyup : true
9073     });
9074 };
9075
9076 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9077      /**
9078      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9079       automatic validation (defaults to "keyup").
9080      */
9081     validationEvent : "keyup",
9082      /**
9083      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9084      */
9085     validateOnBlur : true,
9086     /**
9087      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9088      */
9089     validationDelay : 250,
9090      /**
9091      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9092      */
9093     focusClass : "x-form-focus",  // not needed???
9094     
9095        
9096     /**
9097      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9098      */
9099     invalidClass : "has-warning",
9100     
9101     /**
9102      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9103      */
9104     validClass : "has-success",
9105     
9106     /**
9107      * @cfg {Boolean} hasFeedback (true|false) default true
9108      */
9109     hasFeedback : true,
9110     
9111     /**
9112      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9113      */
9114     invalidFeedbackClass : "glyphicon-warning-sign",
9115     
9116     /**
9117      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9118      */
9119     validFeedbackClass : "glyphicon-ok",
9120     
9121     /**
9122      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9123      */
9124     selectOnFocus : false,
9125     
9126      /**
9127      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9128      */
9129     maskRe : null,
9130        /**
9131      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9132      */
9133     vtype : null,
9134     
9135       /**
9136      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9137      */
9138     disableKeyFilter : false,
9139     
9140        /**
9141      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9142      */
9143     disabled : false,
9144      /**
9145      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9146      */
9147     allowBlank : true,
9148     /**
9149      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9150      */
9151     blankText : "Please complete this mandatory field",
9152     
9153      /**
9154      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9155      */
9156     minLength : 0,
9157     /**
9158      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9159      */
9160     maxLength : Number.MAX_VALUE,
9161     /**
9162      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9163      */
9164     minLengthText : "The minimum length for this field is {0}",
9165     /**
9166      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9167      */
9168     maxLengthText : "The maximum length for this field is {0}",
9169   
9170     
9171     /**
9172      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9173      * If available, this function will be called only after the basic validators all return true, and will be passed the
9174      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9175      */
9176     validator : null,
9177     /**
9178      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9179      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9180      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9181      */
9182     regex : null,
9183     /**
9184      * @cfg {String} regexText -- Depricated - use Invalid Text
9185      */
9186     regexText : "",
9187     
9188     /**
9189      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9190      */
9191     invalidText : "",
9192     
9193     
9194     
9195     autocomplete: false,
9196     
9197     
9198     fieldLabel : '',
9199     inputType : 'text',
9200     
9201     name : false,
9202     placeholder: false,
9203     before : false,
9204     after : false,
9205     size : false,
9206     hasFocus : false,
9207     preventMark: false,
9208     isFormField : true,
9209     value : '',
9210     labelWidth : 2,
9211     labelAlign : false,
9212     readOnly : false,
9213     align : false,
9214     formatedValue : false,
9215     forceFeedback : false,
9216     
9217     indicatorpos : 'left',
9218     
9219     labellg : 0,
9220     labelmd : 0,
9221     labelsm : 0,
9222     labelxs : 0,
9223     
9224     capture : '',
9225     accept : '',
9226     
9227     parentLabelAlign : function()
9228     {
9229         var parent = this;
9230         while (parent.parent()) {
9231             parent = parent.parent();
9232             if (typeof(parent.labelAlign) !='undefined') {
9233                 return parent.labelAlign;
9234             }
9235         }
9236         return 'left';
9237         
9238     },
9239     
9240     getAutoCreate : function()
9241     {
9242         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9243         
9244         var id = Roo.id();
9245         
9246         var cfg = {};
9247         
9248         if(this.inputType != 'hidden'){
9249             cfg.cls = 'form-group' //input-group
9250         }
9251         
9252         var input =  {
9253             tag: 'input',
9254             id : id,
9255             type : this.inputType,
9256             value : this.value,
9257             cls : 'form-control',
9258             placeholder : this.placeholder || '',
9259             autocomplete : this.autocomplete || 'new-password'
9260         };
9261         
9262         if(this.capture.length){
9263             input.capture = this.capture;
9264         }
9265         
9266         if(this.accept.length){
9267             input.accept = this.accept + "/*";
9268         }
9269         
9270         if(this.align){
9271             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9272         }
9273         
9274         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9275             input.maxLength = this.maxLength;
9276         }
9277         
9278         if (this.disabled) {
9279             input.disabled=true;
9280         }
9281         
9282         if (this.readOnly) {
9283             input.readonly=true;
9284         }
9285         
9286         if (this.name) {
9287             input.name = this.name;
9288         }
9289         
9290         if (this.size) {
9291             input.cls += ' input-' + this.size;
9292         }
9293         
9294         var settings=this;
9295         ['xs','sm','md','lg'].map(function(size){
9296             if (settings[size]) {
9297                 cfg.cls += ' col-' + size + '-' + settings[size];
9298             }
9299         });
9300         
9301         var inputblock = input;
9302         
9303         var feedback = {
9304             tag: 'span',
9305             cls: 'glyphicon form-control-feedback'
9306         };
9307             
9308         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9309             
9310             inputblock = {
9311                 cls : 'has-feedback',
9312                 cn :  [
9313                     input,
9314                     feedback
9315                 ] 
9316             };  
9317         }
9318         
9319         if (this.before || this.after) {
9320             
9321             inputblock = {
9322                 cls : 'input-group',
9323                 cn :  [] 
9324             };
9325             
9326             if (this.before && typeof(this.before) == 'string') {
9327                 
9328                 inputblock.cn.push({
9329                     tag :'span',
9330                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9331                     html : this.before
9332                 });
9333             }
9334             if (this.before && typeof(this.before) == 'object') {
9335                 this.before = Roo.factory(this.before);
9336                 
9337                 inputblock.cn.push({
9338                     tag :'span',
9339                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9340                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9341                 });
9342             }
9343             
9344             inputblock.cn.push(input);
9345             
9346             if (this.after && typeof(this.after) == 'string') {
9347                 inputblock.cn.push({
9348                     tag :'span',
9349                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9350                     html : this.after
9351                 });
9352             }
9353             if (this.after && typeof(this.after) == 'object') {
9354                 this.after = Roo.factory(this.after);
9355                 
9356                 inputblock.cn.push({
9357                     tag :'span',
9358                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9359                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9360                 });
9361             }
9362             
9363             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9364                 inputblock.cls += ' has-feedback';
9365                 inputblock.cn.push(feedback);
9366             }
9367         };
9368         var indicator = {
9369             tag : 'i',
9370             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9371             tooltip : 'This field is required'
9372         };
9373         if (Roo.bootstrap.version == 4) {
9374             indicator = {
9375                 tag : 'i',
9376                 style : 'display-none'
9377             };
9378         }
9379         if (align ==='left' && this.fieldLabel.length) {
9380             
9381             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9382             
9383             cfg.cn = [
9384                 indicator,
9385                 {
9386                     tag: 'label',
9387                     'for' :  id,
9388                     cls : 'control-label col-form-label',
9389                     html : this.fieldLabel
9390
9391                 },
9392                 {
9393                     cls : "", 
9394                     cn: [
9395                         inputblock
9396                     ]
9397                 }
9398             ];
9399             
9400             var labelCfg = cfg.cn[1];
9401             var contentCfg = cfg.cn[2];
9402             
9403             if(this.indicatorpos == 'right'){
9404                 cfg.cn = [
9405                     {
9406                         tag: 'label',
9407                         'for' :  id,
9408                         cls : 'control-label col-form-label',
9409                         cn : [
9410                             {
9411                                 tag : 'span',
9412                                 html : this.fieldLabel
9413                             },
9414                             indicator
9415                         ]
9416                     },
9417                     {
9418                         cls : "",
9419                         cn: [
9420                             inputblock
9421                         ]
9422                     }
9423
9424                 ];
9425                 
9426                 labelCfg = cfg.cn[0];
9427                 contentCfg = cfg.cn[1];
9428             
9429             }
9430             
9431             if(this.labelWidth > 12){
9432                 labelCfg.style = "width: " + this.labelWidth + 'px';
9433             }
9434             
9435             if(this.labelWidth < 13 && this.labelmd == 0){
9436                 this.labelmd = this.labelWidth;
9437             }
9438             
9439             if(this.labellg > 0){
9440                 labelCfg.cls += ' col-lg-' + this.labellg;
9441                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9442             }
9443             
9444             if(this.labelmd > 0){
9445                 labelCfg.cls += ' col-md-' + this.labelmd;
9446                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9447             }
9448             
9449             if(this.labelsm > 0){
9450                 labelCfg.cls += ' col-sm-' + this.labelsm;
9451                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9452             }
9453             
9454             if(this.labelxs > 0){
9455                 labelCfg.cls += ' col-xs-' + this.labelxs;
9456                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9457             }
9458             
9459             
9460         } else if ( this.fieldLabel.length) {
9461                 
9462             cfg.cn = [
9463                 {
9464                     tag : 'i',
9465                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9466                     tooltip : 'This field is required'
9467                 },
9468                 {
9469                     tag: 'label',
9470                    //cls : 'input-group-addon',
9471                     html : this.fieldLabel
9472
9473                 },
9474
9475                inputblock
9476
9477            ];
9478            
9479            if(this.indicatorpos == 'right'){
9480                 
9481                 cfg.cn = [
9482                     {
9483                         tag: 'label',
9484                        //cls : 'input-group-addon',
9485                         html : this.fieldLabel
9486
9487                     },
9488                     {
9489                         tag : 'i',
9490                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9491                         tooltip : 'This field is required'
9492                     },
9493
9494                    inputblock
9495
9496                ];
9497
9498             }
9499
9500         } else {
9501             
9502             cfg.cn = [
9503
9504                     inputblock
9505
9506             ];
9507                 
9508                 
9509         };
9510         
9511         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9512            cfg.cls += ' navbar-form';
9513         }
9514         
9515         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9516             // on BS4 we do this only if not form 
9517             cfg.cls += ' navbar-form';
9518             cfg.tag = 'li';
9519         }
9520         
9521         return cfg;
9522         
9523     },
9524     /**
9525      * return the real input element.
9526      */
9527     inputEl: function ()
9528     {
9529         return this.el.select('input.form-control',true).first();
9530     },
9531     
9532     tooltipEl : function()
9533     {
9534         return this.inputEl();
9535     },
9536     
9537     indicatorEl : function()
9538     {
9539         if (Roo.bootstrap.version == 4) {
9540             return false; // not enabled in v4 yet.
9541         }
9542         
9543         var indicator = this.el.select('i.roo-required-indicator',true).first();
9544         
9545         if(!indicator){
9546             return false;
9547         }
9548         
9549         return indicator;
9550         
9551     },
9552     
9553     setDisabled : function(v)
9554     {
9555         var i  = this.inputEl().dom;
9556         if (!v) {
9557             i.removeAttribute('disabled');
9558             return;
9559             
9560         }
9561         i.setAttribute('disabled','true');
9562     },
9563     initEvents : function()
9564     {
9565           
9566         this.inputEl().on("keydown" , this.fireKey,  this);
9567         this.inputEl().on("focus", this.onFocus,  this);
9568         this.inputEl().on("blur", this.onBlur,  this);
9569         
9570         this.inputEl().relayEvent('keyup', this);
9571         
9572         this.indicator = this.indicatorEl();
9573         
9574         if(this.indicator){
9575             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9576         }
9577  
9578         // reference to original value for reset
9579         this.originalValue = this.getValue();
9580         //Roo.form.TextField.superclass.initEvents.call(this);
9581         if(this.validationEvent == 'keyup'){
9582             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9583             this.inputEl().on('keyup', this.filterValidation, this);
9584         }
9585         else if(this.validationEvent !== false){
9586             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9587         }
9588         
9589         if(this.selectOnFocus){
9590             this.on("focus", this.preFocus, this);
9591             
9592         }
9593         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9594             this.inputEl().on("keypress", this.filterKeys, this);
9595         } else {
9596             this.inputEl().relayEvent('keypress', this);
9597         }
9598        /* if(this.grow){
9599             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9600             this.el.on("click", this.autoSize,  this);
9601         }
9602         */
9603         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9604             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9605         }
9606         
9607         if (typeof(this.before) == 'object') {
9608             this.before.render(this.el.select('.roo-input-before',true).first());
9609         }
9610         if (typeof(this.after) == 'object') {
9611             this.after.render(this.el.select('.roo-input-after',true).first());
9612         }
9613         
9614         this.inputEl().on('change', this.onChange, this);
9615         
9616     },
9617     filterValidation : function(e){
9618         if(!e.isNavKeyPress()){
9619             this.validationTask.delay(this.validationDelay);
9620         }
9621     },
9622      /**
9623      * Validates the field value
9624      * @return {Boolean} True if the value is valid, else false
9625      */
9626     validate : function(){
9627         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9628         if(this.disabled || this.validateValue(this.getRawValue())){
9629             this.markValid();
9630             return true;
9631         }
9632         
9633         this.markInvalid();
9634         return false;
9635     },
9636     
9637     
9638     /**
9639      * Validates a value according to the field's validation rules and marks the field as invalid
9640      * if the validation fails
9641      * @param {Mixed} value The value to validate
9642      * @return {Boolean} True if the value is valid, else false
9643      */
9644     validateValue : function(value)
9645     {
9646         if(this.getVisibilityEl().hasClass('hidden')){
9647             return true;
9648         }
9649         
9650         if(value.length < 1)  { // if it's blank
9651             if(this.allowBlank){
9652                 return true;
9653             }
9654             return false;
9655         }
9656         
9657         if(value.length < this.minLength){
9658             return false;
9659         }
9660         if(value.length > this.maxLength){
9661             return false;
9662         }
9663         if(this.vtype){
9664             var vt = Roo.form.VTypes;
9665             if(!vt[this.vtype](value, this)){
9666                 return false;
9667             }
9668         }
9669         if(typeof this.validator == "function"){
9670             var msg = this.validator(value);
9671             if(msg !== true){
9672                 return false;
9673             }
9674             if (typeof(msg) == 'string') {
9675                 this.invalidText = msg;
9676             }
9677         }
9678         
9679         if(this.regex && !this.regex.test(value)){
9680             return false;
9681         }
9682         
9683         return true;
9684     },
9685     
9686      // private
9687     fireKey : function(e){
9688         //Roo.log('field ' + e.getKey());
9689         if(e.isNavKeyPress()){
9690             this.fireEvent("specialkey", this, e);
9691         }
9692     },
9693     focus : function (selectText){
9694         if(this.rendered){
9695             this.inputEl().focus();
9696             if(selectText === true){
9697                 this.inputEl().dom.select();
9698             }
9699         }
9700         return this;
9701     } ,
9702     
9703     onFocus : function(){
9704         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9705            // this.el.addClass(this.focusClass);
9706         }
9707         if(!this.hasFocus){
9708             this.hasFocus = true;
9709             this.startValue = this.getValue();
9710             this.fireEvent("focus", this);
9711         }
9712     },
9713     
9714     beforeBlur : Roo.emptyFn,
9715
9716     
9717     // private
9718     onBlur : function(){
9719         this.beforeBlur();
9720         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9721             //this.el.removeClass(this.focusClass);
9722         }
9723         this.hasFocus = false;
9724         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9725             this.validate();
9726         }
9727         var v = this.getValue();
9728         if(String(v) !== String(this.startValue)){
9729             this.fireEvent('change', this, v, this.startValue);
9730         }
9731         this.fireEvent("blur", this);
9732     },
9733     
9734     onChange : function(e)
9735     {
9736         var v = this.getValue();
9737         if(String(v) !== String(this.startValue)){
9738             this.fireEvent('change', this, v, this.startValue);
9739         }
9740         
9741     },
9742     
9743     /**
9744      * Resets the current field value to the originally loaded value and clears any validation messages
9745      */
9746     reset : function(){
9747         this.setValue(this.originalValue);
9748         this.validate();
9749     },
9750      /**
9751      * Returns the name of the field
9752      * @return {Mixed} name The name field
9753      */
9754     getName: function(){
9755         return this.name;
9756     },
9757      /**
9758      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9759      * @return {Mixed} value The field value
9760      */
9761     getValue : function(){
9762         
9763         var v = this.inputEl().getValue();
9764         
9765         return v;
9766     },
9767     /**
9768      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9769      * @return {Mixed} value The field value
9770      */
9771     getRawValue : function(){
9772         var v = this.inputEl().getValue();
9773         
9774         return v;
9775     },
9776     
9777     /**
9778      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9779      * @param {Mixed} value The value to set
9780      */
9781     setRawValue : function(v){
9782         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9783     },
9784     
9785     selectText : function(start, end){
9786         var v = this.getRawValue();
9787         if(v.length > 0){
9788             start = start === undefined ? 0 : start;
9789             end = end === undefined ? v.length : end;
9790             var d = this.inputEl().dom;
9791             if(d.setSelectionRange){
9792                 d.setSelectionRange(start, end);
9793             }else if(d.createTextRange){
9794                 var range = d.createTextRange();
9795                 range.moveStart("character", start);
9796                 range.moveEnd("character", v.length-end);
9797                 range.select();
9798             }
9799         }
9800     },
9801     
9802     /**
9803      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9804      * @param {Mixed} value The value to set
9805      */
9806     setValue : function(v){
9807         this.value = v;
9808         if(this.rendered){
9809             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9810             this.validate();
9811         }
9812     },
9813     
9814     /*
9815     processValue : function(value){
9816         if(this.stripCharsRe){
9817             var newValue = value.replace(this.stripCharsRe, '');
9818             if(newValue !== value){
9819                 this.setRawValue(newValue);
9820                 return newValue;
9821             }
9822         }
9823         return value;
9824     },
9825   */
9826     preFocus : function(){
9827         
9828         if(this.selectOnFocus){
9829             this.inputEl().dom.select();
9830         }
9831     },
9832     filterKeys : function(e){
9833         var k = e.getKey();
9834         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9835             return;
9836         }
9837         var c = e.getCharCode(), cc = String.fromCharCode(c);
9838         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9839             return;
9840         }
9841         if(!this.maskRe.test(cc)){
9842             e.stopEvent();
9843         }
9844     },
9845      /**
9846      * Clear any invalid styles/messages for this field
9847      */
9848     clearInvalid : function(){
9849         
9850         if(!this.el || this.preventMark){ // not rendered
9851             return;
9852         }
9853         
9854         
9855         this.el.removeClass([this.invalidClass, 'is-invalid']);
9856         
9857         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9858             
9859             var feedback = this.el.select('.form-control-feedback', true).first();
9860             
9861             if(feedback){
9862                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9863             }
9864             
9865         }
9866         
9867         if(this.indicator){
9868             this.indicator.removeClass('visible');
9869             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9870         }
9871         
9872         this.fireEvent('valid', this);
9873     },
9874     
9875      /**
9876      * Mark this field as valid
9877      */
9878     markValid : function()
9879     {
9880         if(!this.el  || this.preventMark){ // not rendered...
9881             return;
9882         }
9883         
9884         this.el.removeClass([this.invalidClass, this.validClass]);
9885         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9886
9887         var feedback = this.el.select('.form-control-feedback', true).first();
9888             
9889         if(feedback){
9890             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9891         }
9892         
9893         if(this.indicator){
9894             this.indicator.removeClass('visible');
9895             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9896         }
9897         
9898         if(this.disabled){
9899             return;
9900         }
9901         
9902         if(this.allowBlank && !this.getRawValue().length){
9903             return;
9904         }
9905         if (Roo.bootstrap.version == 3) {
9906             this.el.addClass(this.validClass);
9907         } else {
9908             this.inputEl().addClass('is-valid');
9909         }
9910
9911         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
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([this.invalidFeedbackClass, this.validFeedbackClass]);
9917                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9918             }
9919             
9920         }
9921         
9922         this.fireEvent('valid', this);
9923     },
9924     
9925      /**
9926      * Mark this field as invalid
9927      * @param {String} msg The validation message
9928      */
9929     markInvalid : function(msg)
9930     {
9931         if(!this.el  || this.preventMark){ // not rendered
9932             return;
9933         }
9934         
9935         this.el.removeClass([this.invalidClass, this.validClass]);
9936         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9937         
9938         var feedback = this.el.select('.form-control-feedback', true).first();
9939             
9940         if(feedback){
9941             this.el.select('.form-control-feedback', true).first().removeClass(
9942                     [this.invalidFeedbackClass, this.validFeedbackClass]);
9943         }
9944
9945         if(this.disabled){
9946             return;
9947         }
9948         
9949         if(this.allowBlank && !this.getRawValue().length){
9950             return;
9951         }
9952         
9953         if(this.indicator){
9954             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9955             this.indicator.addClass('visible');
9956         }
9957         if (Roo.bootstrap.version == 3) {
9958             this.el.addClass(this.invalidClass);
9959         } else {
9960             this.inputEl().addClass('is-invalid');
9961         }
9962         
9963         
9964         
9965         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9966             
9967             var feedback = this.el.select('.form-control-feedback', true).first();
9968             
9969             if(feedback){
9970                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9971                 
9972                 if(this.getValue().length || this.forceFeedback){
9973                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9974                 }
9975                 
9976             }
9977             
9978         }
9979         
9980         this.fireEvent('invalid', this, msg);
9981     },
9982     // private
9983     SafariOnKeyDown : function(event)
9984     {
9985         // this is a workaround for a password hang bug on chrome/ webkit.
9986         if (this.inputEl().dom.type != 'password') {
9987             return;
9988         }
9989         
9990         var isSelectAll = false;
9991         
9992         if(this.inputEl().dom.selectionEnd > 0){
9993             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9994         }
9995         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9996             event.preventDefault();
9997             this.setValue('');
9998             return;
9999         }
10000         
10001         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
10002             
10003             event.preventDefault();
10004             // this is very hacky as keydown always get's upper case.
10005             //
10006             var cc = String.fromCharCode(event.getCharCode());
10007             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
10008             
10009         }
10010     },
10011     adjustWidth : function(tag, w){
10012         tag = tag.toLowerCase();
10013         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
10014             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
10015                 if(tag == 'input'){
10016                     return w + 2;
10017                 }
10018                 if(tag == 'textarea'){
10019                     return w-2;
10020                 }
10021             }else if(Roo.isOpera){
10022                 if(tag == 'input'){
10023                     return w + 2;
10024                 }
10025                 if(tag == 'textarea'){
10026                     return w-2;
10027                 }
10028             }
10029         }
10030         return w;
10031     },
10032     
10033     setFieldLabel : function(v)
10034     {
10035         if(!this.rendered){
10036             return;
10037         }
10038         
10039         if(this.indicatorEl()){
10040             var ar = this.el.select('label > span',true);
10041             
10042             if (ar.elements.length) {
10043                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10044                 this.fieldLabel = v;
10045                 return;
10046             }
10047             
10048             var br = this.el.select('label',true);
10049             
10050             if(br.elements.length) {
10051                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10052                 this.fieldLabel = v;
10053                 return;
10054             }
10055             
10056             Roo.log('Cannot Found any of label > span || label in input');
10057             return;
10058         }
10059         
10060         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10061         this.fieldLabel = v;
10062         
10063         
10064     }
10065 });
10066
10067  
10068 /*
10069  * - LGPL
10070  *
10071  * Input
10072  * 
10073  */
10074
10075 /**
10076  * @class Roo.bootstrap.TextArea
10077  * @extends Roo.bootstrap.Input
10078  * Bootstrap TextArea class
10079  * @cfg {Number} cols Specifies the visible width of a text area
10080  * @cfg {Number} rows Specifies the visible number of lines in a text area
10081  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10082  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10083  * @cfg {string} html text
10084  * 
10085  * @constructor
10086  * Create a new TextArea
10087  * @param {Object} config The config object
10088  */
10089
10090 Roo.bootstrap.TextArea = function(config){
10091     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10092    
10093 };
10094
10095 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10096      
10097     cols : false,
10098     rows : 5,
10099     readOnly : false,
10100     warp : 'soft',
10101     resize : false,
10102     value: false,
10103     html: false,
10104     
10105     getAutoCreate : function(){
10106         
10107         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10108         
10109         var id = Roo.id();
10110         
10111         var cfg = {};
10112         
10113         if(this.inputType != 'hidden'){
10114             cfg.cls = 'form-group' //input-group
10115         }
10116         
10117         var input =  {
10118             tag: 'textarea',
10119             id : id,
10120             warp : this.warp,
10121             rows : this.rows,
10122             value : this.value || '',
10123             html: this.html || '',
10124             cls : 'form-control',
10125             placeholder : this.placeholder || '' 
10126             
10127         };
10128         
10129         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10130             input.maxLength = this.maxLength;
10131         }
10132         
10133         if(this.resize){
10134             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10135         }
10136         
10137         if(this.cols){
10138             input.cols = this.cols;
10139         }
10140         
10141         if (this.readOnly) {
10142             input.readonly = true;
10143         }
10144         
10145         if (this.name) {
10146             input.name = this.name;
10147         }
10148         
10149         if (this.size) {
10150             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10151         }
10152         
10153         var settings=this;
10154         ['xs','sm','md','lg'].map(function(size){
10155             if (settings[size]) {
10156                 cfg.cls += ' col-' + size + '-' + settings[size];
10157             }
10158         });
10159         
10160         var inputblock = input;
10161         
10162         if(this.hasFeedback && !this.allowBlank){
10163             
10164             var feedback = {
10165                 tag: 'span',
10166                 cls: 'glyphicon form-control-feedback'
10167             };
10168
10169             inputblock = {
10170                 cls : 'has-feedback',
10171                 cn :  [
10172                     input,
10173                     feedback
10174                 ] 
10175             };  
10176         }
10177         
10178         
10179         if (this.before || this.after) {
10180             
10181             inputblock = {
10182                 cls : 'input-group',
10183                 cn :  [] 
10184             };
10185             if (this.before) {
10186                 inputblock.cn.push({
10187                     tag :'span',
10188                     cls : 'input-group-addon',
10189                     html : this.before
10190                 });
10191             }
10192             
10193             inputblock.cn.push(input);
10194             
10195             if(this.hasFeedback && !this.allowBlank){
10196                 inputblock.cls += ' has-feedback';
10197                 inputblock.cn.push(feedback);
10198             }
10199             
10200             if (this.after) {
10201                 inputblock.cn.push({
10202                     tag :'span',
10203                     cls : 'input-group-addon',
10204                     html : this.after
10205                 });
10206             }
10207             
10208         }
10209         
10210         if (align ==='left' && this.fieldLabel.length) {
10211             cfg.cn = [
10212                 {
10213                     tag: 'label',
10214                     'for' :  id,
10215                     cls : 'control-label',
10216                     html : this.fieldLabel
10217                 },
10218                 {
10219                     cls : "",
10220                     cn: [
10221                         inputblock
10222                     ]
10223                 }
10224
10225             ];
10226             
10227             if(this.labelWidth > 12){
10228                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10229             }
10230
10231             if(this.labelWidth < 13 && this.labelmd == 0){
10232                 this.labelmd = this.labelWidth;
10233             }
10234
10235             if(this.labellg > 0){
10236                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10237                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10238             }
10239
10240             if(this.labelmd > 0){
10241                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10242                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10243             }
10244
10245             if(this.labelsm > 0){
10246                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10247                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10248             }
10249
10250             if(this.labelxs > 0){
10251                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10252                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10253             }
10254             
10255         } else if ( this.fieldLabel.length) {
10256             cfg.cn = [
10257
10258                {
10259                    tag: 'label',
10260                    //cls : 'input-group-addon',
10261                    html : this.fieldLabel
10262
10263                },
10264
10265                inputblock
10266
10267            ];
10268
10269         } else {
10270
10271             cfg.cn = [
10272
10273                 inputblock
10274
10275             ];
10276                 
10277         }
10278         
10279         if (this.disabled) {
10280             input.disabled=true;
10281         }
10282         
10283         return cfg;
10284         
10285     },
10286     /**
10287      * return the real textarea element.
10288      */
10289     inputEl: function ()
10290     {
10291         return this.el.select('textarea.form-control',true).first();
10292     },
10293     
10294     /**
10295      * Clear any invalid styles/messages for this field
10296      */
10297     clearInvalid : function()
10298     {
10299         
10300         if(!this.el || this.preventMark){ // not rendered
10301             return;
10302         }
10303         
10304         var label = this.el.select('label', true).first();
10305         var icon = this.el.select('i.fa-star', true).first();
10306         
10307         if(label && icon){
10308             icon.remove();
10309         }
10310         this.el.removeClass( this.validClass);
10311         this.inputEl().removeClass('is-invalid');
10312          
10313         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10314             
10315             var feedback = this.el.select('.form-control-feedback', true).first();
10316             
10317             if(feedback){
10318                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10319             }
10320             
10321         }
10322         
10323         this.fireEvent('valid', this);
10324     },
10325     
10326      /**
10327      * Mark this field as valid
10328      */
10329     markValid : function()
10330     {
10331         if(!this.el  || this.preventMark){ // not rendered
10332             return;
10333         }
10334         
10335         this.el.removeClass([this.invalidClass, this.validClass]);
10336         this.inputEl().removeClass(['is-valid', 'is-invalid']);
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         }
10343
10344         if(this.disabled || this.allowBlank){
10345             return;
10346         }
10347         
10348         var label = this.el.select('label', true).first();
10349         var icon = this.el.select('i.fa-star', true).first();
10350         
10351         if(label && icon){
10352             icon.remove();
10353         }
10354         if (Roo.bootstrap.version == 3) {
10355             this.el.addClass(this.validClass);
10356         } else {
10357             this.inputEl().addClass('is-valid');
10358         }
10359         
10360         
10361         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
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                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10368             }
10369             
10370         }
10371         
10372         this.fireEvent('valid', this);
10373     },
10374     
10375      /**
10376      * Mark this field as invalid
10377      * @param {String} msg The validation message
10378      */
10379     markInvalid : function(msg)
10380     {
10381         if(!this.el  || this.preventMark){ // not rendered
10382             return;
10383         }
10384         
10385         this.el.removeClass([this.invalidClass, this.validClass]);
10386         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10387         
10388         var feedback = this.el.select('.form-control-feedback', true).first();
10389             
10390         if(feedback){
10391             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10392         }
10393
10394         if(this.disabled || this.allowBlank){
10395             return;
10396         }
10397         
10398         var label = this.el.select('label', true).first();
10399         var icon = this.el.select('i.fa-star', true).first();
10400         
10401         if(!this.getValue().length && label && !icon){
10402             this.el.createChild({
10403                 tag : 'i',
10404                 cls : 'text-danger fa fa-lg fa-star',
10405                 tooltip : 'This field is required',
10406                 style : 'margin-right:5px;'
10407             }, label, true);
10408         }
10409         
10410         if (Roo.bootstrap.version == 3) {
10411             this.el.addClass(this.invalidClass);
10412         } else {
10413             this.inputEl().addClass('is-invalid');
10414         }
10415         
10416         // fixme ... this may be depricated need to test..
10417         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10418             
10419             var feedback = this.el.select('.form-control-feedback', true).first();
10420             
10421             if(feedback){
10422                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10423                 
10424                 if(this.getValue().length || this.forceFeedback){
10425                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10426                 }
10427                 
10428             }
10429             
10430         }
10431         
10432         this.fireEvent('invalid', this, msg);
10433     }
10434 });
10435
10436  
10437 /*
10438  * - LGPL
10439  *
10440  * trigger field - base class for combo..
10441  * 
10442  */
10443  
10444 /**
10445  * @class Roo.bootstrap.TriggerField
10446  * @extends Roo.bootstrap.Input
10447  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10448  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10449  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10450  * for which you can provide a custom implementation.  For example:
10451  * <pre><code>
10452 var trigger = new Roo.bootstrap.TriggerField();
10453 trigger.onTriggerClick = myTriggerFn;
10454 trigger.applyTo('my-field');
10455 </code></pre>
10456  *
10457  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10458  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10459  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10460  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10461  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
10462
10463  * @constructor
10464  * Create a new TriggerField.
10465  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10466  * to the base TextField)
10467  */
10468 Roo.bootstrap.TriggerField = function(config){
10469     this.mimicing = false;
10470     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10471 };
10472
10473 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10474     /**
10475      * @cfg {String} triggerClass A CSS class to apply to the trigger
10476      */
10477      /**
10478      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10479      */
10480     hideTrigger:false,
10481
10482     /**
10483      * @cfg {Boolean} removable (true|false) special filter default false
10484      */
10485     removable : false,
10486     
10487     /** @cfg {Boolean} grow @hide */
10488     /** @cfg {Number} growMin @hide */
10489     /** @cfg {Number} growMax @hide */
10490
10491     /**
10492      * @hide 
10493      * @method
10494      */
10495     autoSize: Roo.emptyFn,
10496     // private
10497     monitorTab : true,
10498     // private
10499     deferHeight : true,
10500
10501     
10502     actionMode : 'wrap',
10503     
10504     caret : false,
10505     
10506     
10507     getAutoCreate : function(){
10508        
10509         var align = this.labelAlign || this.parentLabelAlign();
10510         
10511         var id = Roo.id();
10512         
10513         var cfg = {
10514             cls: 'form-group' //input-group
10515         };
10516         
10517         
10518         var input =  {
10519             tag: 'input',
10520             id : id,
10521             type : this.inputType,
10522             cls : 'form-control',
10523             autocomplete: 'new-password',
10524             placeholder : this.placeholder || '' 
10525             
10526         };
10527         if (this.name) {
10528             input.name = this.name;
10529         }
10530         if (this.size) {
10531             input.cls += ' input-' + this.size;
10532         }
10533         
10534         if (this.disabled) {
10535             input.disabled=true;
10536         }
10537         
10538         var inputblock = input;
10539         
10540         if(this.hasFeedback && !this.allowBlank){
10541             
10542             var feedback = {
10543                 tag: 'span',
10544                 cls: 'glyphicon form-control-feedback'
10545             };
10546             
10547             if(this.removable && !this.editable && !this.tickable){
10548                 inputblock = {
10549                     cls : 'has-feedback',
10550                     cn :  [
10551                         inputblock,
10552                         {
10553                             tag: 'button',
10554                             html : 'x',
10555                             cls : 'roo-combo-removable-btn close'
10556                         },
10557                         feedback
10558                     ] 
10559                 };
10560             } else {
10561                 inputblock = {
10562                     cls : 'has-feedback',
10563                     cn :  [
10564                         inputblock,
10565                         feedback
10566                     ] 
10567                 };
10568             }
10569
10570         } else {
10571             if(this.removable && !this.editable && !this.tickable){
10572                 inputblock = {
10573                     cls : 'roo-removable',
10574                     cn :  [
10575                         inputblock,
10576                         {
10577                             tag: 'button',
10578                             html : 'x',
10579                             cls : 'roo-combo-removable-btn close'
10580                         }
10581                     ] 
10582                 };
10583             }
10584         }
10585         
10586         if (this.before || this.after) {
10587             
10588             inputblock = {
10589                 cls : 'input-group',
10590                 cn :  [] 
10591             };
10592             if (this.before) {
10593                 inputblock.cn.push({
10594                     tag :'span',
10595                     cls : 'input-group-addon input-group-prepend input-group-text',
10596                     html : this.before
10597                 });
10598             }
10599             
10600             inputblock.cn.push(input);
10601             
10602             if(this.hasFeedback && !this.allowBlank){
10603                 inputblock.cls += ' has-feedback';
10604                 inputblock.cn.push(feedback);
10605             }
10606             
10607             if (this.after) {
10608                 inputblock.cn.push({
10609                     tag :'span',
10610                     cls : 'input-group-addon input-group-append input-group-text',
10611                     html : this.after
10612                 });
10613             }
10614             
10615         };
10616         
10617       
10618         
10619         var ibwrap = inputblock;
10620         
10621         if(this.multiple){
10622             ibwrap = {
10623                 tag: 'ul',
10624                 cls: 'roo-select2-choices',
10625                 cn:[
10626                     {
10627                         tag: 'li',
10628                         cls: 'roo-select2-search-field',
10629                         cn: [
10630
10631                             inputblock
10632                         ]
10633                     }
10634                 ]
10635             };
10636                 
10637         }
10638         
10639         var combobox = {
10640             cls: 'roo-select2-container input-group',
10641             cn: [
10642                  {
10643                     tag: 'input',
10644                     type : 'hidden',
10645                     cls: 'form-hidden-field'
10646                 },
10647                 ibwrap
10648             ]
10649         };
10650         
10651         if(!this.multiple && this.showToggleBtn){
10652             
10653             var caret = {
10654                         tag: 'span',
10655                         cls: 'caret'
10656              };
10657             if (this.caret != false) {
10658                 caret = {
10659                      tag: 'i',
10660                      cls: 'fa fa-' + this.caret
10661                 };
10662                 
10663             }
10664             
10665             combobox.cn.push({
10666                 tag :'span',
10667                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10668                 cn : [
10669                     Roo.bootstrap.version == 3 ? caret : '',
10670                     {
10671                         tag: 'span',
10672                         cls: 'combobox-clear',
10673                         cn  : [
10674                             {
10675                                 tag : 'i',
10676                                 cls: 'icon-remove'
10677                             }
10678                         ]
10679                     }
10680                 ]
10681
10682             })
10683         }
10684         
10685         if(this.multiple){
10686             combobox.cls += ' roo-select2-container-multi';
10687         }
10688          var indicator = {
10689             tag : 'i',
10690             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10691             tooltip : 'This field is required'
10692         };
10693         if (Roo.bootstrap.version == 4) {
10694             indicator = {
10695                 tag : 'i',
10696                 style : 'display:none'
10697             };
10698         }
10699         
10700         
10701         if (align ==='left' && this.fieldLabel.length) {
10702             
10703             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10704
10705             cfg.cn = [
10706                 indicator,
10707                 {
10708                     tag: 'label',
10709                     'for' :  id,
10710                     cls : 'control-label',
10711                     html : this.fieldLabel
10712
10713                 },
10714                 {
10715                     cls : "", 
10716                     cn: [
10717                         combobox
10718                     ]
10719                 }
10720
10721             ];
10722             
10723             var labelCfg = cfg.cn[1];
10724             var contentCfg = cfg.cn[2];
10725             
10726             if(this.indicatorpos == 'right'){
10727                 cfg.cn = [
10728                     {
10729                         tag: 'label',
10730                         'for' :  id,
10731                         cls : 'control-label',
10732                         cn : [
10733                             {
10734                                 tag : 'span',
10735                                 html : this.fieldLabel
10736                             },
10737                             indicator
10738                         ]
10739                     },
10740                     {
10741                         cls : "", 
10742                         cn: [
10743                             combobox
10744                         ]
10745                     }
10746
10747                 ];
10748                 
10749                 labelCfg = cfg.cn[0];
10750                 contentCfg = cfg.cn[1];
10751             }
10752             
10753             if(this.labelWidth > 12){
10754                 labelCfg.style = "width: " + this.labelWidth + 'px';
10755             }
10756             
10757             if(this.labelWidth < 13 && this.labelmd == 0){
10758                 this.labelmd = this.labelWidth;
10759             }
10760             
10761             if(this.labellg > 0){
10762                 labelCfg.cls += ' col-lg-' + this.labellg;
10763                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10764             }
10765             
10766             if(this.labelmd > 0){
10767                 labelCfg.cls += ' col-md-' + this.labelmd;
10768                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10769             }
10770             
10771             if(this.labelsm > 0){
10772                 labelCfg.cls += ' col-sm-' + this.labelsm;
10773                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10774             }
10775             
10776             if(this.labelxs > 0){
10777                 labelCfg.cls += ' col-xs-' + this.labelxs;
10778                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10779             }
10780             
10781         } else if ( this.fieldLabel.length) {
10782 //                Roo.log(" label");
10783             cfg.cn = [
10784                 indicator,
10785                {
10786                    tag: 'label',
10787                    //cls : 'input-group-addon',
10788                    html : this.fieldLabel
10789
10790                },
10791
10792                combobox
10793
10794             ];
10795             
10796             if(this.indicatorpos == 'right'){
10797                 
10798                 cfg.cn = [
10799                     {
10800                        tag: 'label',
10801                        cn : [
10802                            {
10803                                tag : 'span',
10804                                html : this.fieldLabel
10805                            },
10806                            indicator
10807                        ]
10808
10809                     },
10810                     combobox
10811
10812                 ];
10813
10814             }
10815
10816         } else {
10817             
10818 //                Roo.log(" no label && no align");
10819                 cfg = combobox
10820                      
10821                 
10822         }
10823         
10824         var settings=this;
10825         ['xs','sm','md','lg'].map(function(size){
10826             if (settings[size]) {
10827                 cfg.cls += ' col-' + size + '-' + settings[size];
10828             }
10829         });
10830         
10831         return cfg;
10832         
10833     },
10834     
10835     
10836     
10837     // private
10838     onResize : function(w, h){
10839 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10840 //        if(typeof w == 'number'){
10841 //            var x = w - this.trigger.getWidth();
10842 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10843 //            this.trigger.setStyle('left', x+'px');
10844 //        }
10845     },
10846
10847     // private
10848     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10849
10850     // private
10851     getResizeEl : function(){
10852         return this.inputEl();
10853     },
10854
10855     // private
10856     getPositionEl : function(){
10857         return this.inputEl();
10858     },
10859
10860     // private
10861     alignErrorIcon : function(){
10862         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10863     },
10864
10865     // private
10866     initEvents : function(){
10867         
10868         this.createList();
10869         
10870         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10871         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10872         if(!this.multiple && this.showToggleBtn){
10873             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10874             if(this.hideTrigger){
10875                 this.trigger.setDisplayed(false);
10876             }
10877             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10878         }
10879         
10880         if(this.multiple){
10881             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10882         }
10883         
10884         if(this.removable && !this.editable && !this.tickable){
10885             var close = this.closeTriggerEl();
10886             
10887             if(close){
10888                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10889                 close.on('click', this.removeBtnClick, this, close);
10890             }
10891         }
10892         
10893         //this.trigger.addClassOnOver('x-form-trigger-over');
10894         //this.trigger.addClassOnClick('x-form-trigger-click');
10895         
10896         //if(!this.width){
10897         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10898         //}
10899     },
10900     
10901     closeTriggerEl : function()
10902     {
10903         var close = this.el.select('.roo-combo-removable-btn', true).first();
10904         return close ? close : false;
10905     },
10906     
10907     removeBtnClick : function(e, h, el)
10908     {
10909         e.preventDefault();
10910         
10911         if(this.fireEvent("remove", this) !== false){
10912             this.reset();
10913             this.fireEvent("afterremove", this)
10914         }
10915     },
10916     
10917     createList : function()
10918     {
10919         this.list = Roo.get(document.body).createChild({
10920             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10921             cls: 'typeahead typeahead-long dropdown-menu',
10922             style: 'display:none'
10923         });
10924         
10925         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10926         
10927     },
10928
10929     // private
10930     initTrigger : function(){
10931        
10932     },
10933
10934     // private
10935     onDestroy : function(){
10936         if(this.trigger){
10937             this.trigger.removeAllListeners();
10938           //  this.trigger.remove();
10939         }
10940         //if(this.wrap){
10941         //    this.wrap.remove();
10942         //}
10943         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10944     },
10945
10946     // private
10947     onFocus : function(){
10948         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10949         /*
10950         if(!this.mimicing){
10951             this.wrap.addClass('x-trigger-wrap-focus');
10952             this.mimicing = true;
10953             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10954             if(this.monitorTab){
10955                 this.el.on("keydown", this.checkTab, this);
10956             }
10957         }
10958         */
10959     },
10960
10961     // private
10962     checkTab : function(e){
10963         if(e.getKey() == e.TAB){
10964             this.triggerBlur();
10965         }
10966     },
10967
10968     // private
10969     onBlur : function(){
10970         // do nothing
10971     },
10972
10973     // private
10974     mimicBlur : function(e, t){
10975         /*
10976         if(!this.wrap.contains(t) && this.validateBlur()){
10977             this.triggerBlur();
10978         }
10979         */
10980     },
10981
10982     // private
10983     triggerBlur : function(){
10984         this.mimicing = false;
10985         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10986         if(this.monitorTab){
10987             this.el.un("keydown", this.checkTab, this);
10988         }
10989         //this.wrap.removeClass('x-trigger-wrap-focus');
10990         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10991     },
10992
10993     // private
10994     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10995     validateBlur : function(e, t){
10996         return true;
10997     },
10998
10999     // private
11000     onDisable : function(){
11001         this.inputEl().dom.disabled = true;
11002         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
11003         //if(this.wrap){
11004         //    this.wrap.addClass('x-item-disabled');
11005         //}
11006     },
11007
11008     // private
11009     onEnable : function(){
11010         this.inputEl().dom.disabled = false;
11011         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
11012         //if(this.wrap){
11013         //    this.el.removeClass('x-item-disabled');
11014         //}
11015     },
11016
11017     // private
11018     onShow : function(){
11019         var ae = this.getActionEl();
11020         
11021         if(ae){
11022             ae.dom.style.display = '';
11023             ae.dom.style.visibility = 'visible';
11024         }
11025     },
11026
11027     // private
11028     
11029     onHide : function(){
11030         var ae = this.getActionEl();
11031         ae.dom.style.display = 'none';
11032     },
11033
11034     /**
11035      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
11036      * by an implementing function.
11037      * @method
11038      * @param {EventObject} e
11039      */
11040     onTriggerClick : Roo.emptyFn
11041 });
11042  /*
11043  * Based on:
11044  * Ext JS Library 1.1.1
11045  * Copyright(c) 2006-2007, Ext JS, LLC.
11046  *
11047  * Originally Released Under LGPL - original licence link has changed is not relivant.
11048  *
11049  * Fork - LGPL
11050  * <script type="text/javascript">
11051  */
11052
11053
11054 /**
11055  * @class Roo.data.SortTypes
11056  * @singleton
11057  * Defines the default sorting (casting?) comparison functions used when sorting data.
11058  */
11059 Roo.data.SortTypes = {
11060     /**
11061      * Default sort that does nothing
11062      * @param {Mixed} s The value being converted
11063      * @return {Mixed} The comparison value
11064      */
11065     none : function(s){
11066         return s;
11067     },
11068     
11069     /**
11070      * The regular expression used to strip tags
11071      * @type {RegExp}
11072      * @property
11073      */
11074     stripTagsRE : /<\/?[^>]+>/gi,
11075     
11076     /**
11077      * Strips all HTML tags to sort on text only
11078      * @param {Mixed} s The value being converted
11079      * @return {String} The comparison value
11080      */
11081     asText : function(s){
11082         return String(s).replace(this.stripTagsRE, "");
11083     },
11084     
11085     /**
11086      * Strips all HTML tags to sort on text only - Case insensitive
11087      * @param {Mixed} s The value being converted
11088      * @return {String} The comparison value
11089      */
11090     asUCText : function(s){
11091         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11092     },
11093     
11094     /**
11095      * Case insensitive string
11096      * @param {Mixed} s The value being converted
11097      * @return {String} The comparison value
11098      */
11099     asUCString : function(s) {
11100         return String(s).toUpperCase();
11101     },
11102     
11103     /**
11104      * Date sorting
11105      * @param {Mixed} s The value being converted
11106      * @return {Number} The comparison value
11107      */
11108     asDate : function(s) {
11109         if(!s){
11110             return 0;
11111         }
11112         if(s instanceof Date){
11113             return s.getTime();
11114         }
11115         return Date.parse(String(s));
11116     },
11117     
11118     /**
11119      * Float sorting
11120      * @param {Mixed} s The value being converted
11121      * @return {Float} The comparison value
11122      */
11123     asFloat : function(s) {
11124         var val = parseFloat(String(s).replace(/,/g, ""));
11125         if(isNaN(val)) {
11126             val = 0;
11127         }
11128         return val;
11129     },
11130     
11131     /**
11132      * Integer sorting
11133      * @param {Mixed} s The value being converted
11134      * @return {Number} The comparison value
11135      */
11136     asInt : function(s) {
11137         var val = parseInt(String(s).replace(/,/g, ""));
11138         if(isNaN(val)) {
11139             val = 0;
11140         }
11141         return val;
11142     }
11143 };/*
11144  * Based on:
11145  * Ext JS Library 1.1.1
11146  * Copyright(c) 2006-2007, Ext JS, LLC.
11147  *
11148  * Originally Released Under LGPL - original licence link has changed is not relivant.
11149  *
11150  * Fork - LGPL
11151  * <script type="text/javascript">
11152  */
11153
11154 /**
11155 * @class Roo.data.Record
11156  * Instances of this class encapsulate both record <em>definition</em> information, and record
11157  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11158  * to access Records cached in an {@link Roo.data.Store} object.<br>
11159  * <p>
11160  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11161  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11162  * objects.<br>
11163  * <p>
11164  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11165  * @constructor
11166  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11167  * {@link #create}. The parameters are the same.
11168  * @param {Array} data An associative Array of data values keyed by the field name.
11169  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11170  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11171  * not specified an integer id is generated.
11172  */
11173 Roo.data.Record = function(data, id){
11174     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11175     this.data = data;
11176 };
11177
11178 /**
11179  * Generate a constructor for a specific record layout.
11180  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11181  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11182  * Each field definition object may contain the following properties: <ul>
11183  * <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,
11184  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11185  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11186  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11187  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11188  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11189  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11190  * this may be omitted.</p></li>
11191  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11192  * <ul><li>auto (Default, implies no conversion)</li>
11193  * <li>string</li>
11194  * <li>int</li>
11195  * <li>float</li>
11196  * <li>boolean</li>
11197  * <li>date</li></ul></p></li>
11198  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11199  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11200  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11201  * by the Reader into an object that will be stored in the Record. It is passed the
11202  * following parameters:<ul>
11203  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11204  * </ul></p></li>
11205  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11206  * </ul>
11207  * <br>usage:<br><pre><code>
11208 var TopicRecord = Roo.data.Record.create(
11209     {name: 'title', mapping: 'topic_title'},
11210     {name: 'author', mapping: 'username'},
11211     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11212     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11213     {name: 'lastPoster', mapping: 'user2'},
11214     {name: 'excerpt', mapping: 'post_text'}
11215 );
11216
11217 var myNewRecord = new TopicRecord({
11218     title: 'Do my job please',
11219     author: 'noobie',
11220     totalPosts: 1,
11221     lastPost: new Date(),
11222     lastPoster: 'Animal',
11223     excerpt: 'No way dude!'
11224 });
11225 myStore.add(myNewRecord);
11226 </code></pre>
11227  * @method create
11228  * @static
11229  */
11230 Roo.data.Record.create = function(o){
11231     var f = function(){
11232         f.superclass.constructor.apply(this, arguments);
11233     };
11234     Roo.extend(f, Roo.data.Record);
11235     var p = f.prototype;
11236     p.fields = new Roo.util.MixedCollection(false, function(field){
11237         return field.name;
11238     });
11239     for(var i = 0, len = o.length; i < len; i++){
11240         p.fields.add(new Roo.data.Field(o[i]));
11241     }
11242     f.getField = function(name){
11243         return p.fields.get(name);  
11244     };
11245     return f;
11246 };
11247
11248 Roo.data.Record.AUTO_ID = 1000;
11249 Roo.data.Record.EDIT = 'edit';
11250 Roo.data.Record.REJECT = 'reject';
11251 Roo.data.Record.COMMIT = 'commit';
11252
11253 Roo.data.Record.prototype = {
11254     /**
11255      * Readonly flag - true if this record has been modified.
11256      * @type Boolean
11257      */
11258     dirty : false,
11259     editing : false,
11260     error: null,
11261     modified: null,
11262
11263     // private
11264     join : function(store){
11265         this.store = store;
11266     },
11267
11268     /**
11269      * Set the named field to the specified value.
11270      * @param {String} name The name of the field to set.
11271      * @param {Object} value The value to set the field to.
11272      */
11273     set : function(name, value){
11274         if(this.data[name] == value){
11275             return;
11276         }
11277         this.dirty = true;
11278         if(!this.modified){
11279             this.modified = {};
11280         }
11281         if(typeof this.modified[name] == 'undefined'){
11282             this.modified[name] = this.data[name];
11283         }
11284         this.data[name] = value;
11285         if(!this.editing && this.store){
11286             this.store.afterEdit(this);
11287         }       
11288     },
11289
11290     /**
11291      * Get the value of the named field.
11292      * @param {String} name The name of the field to get the value of.
11293      * @return {Object} The value of the field.
11294      */
11295     get : function(name){
11296         return this.data[name]; 
11297     },
11298
11299     // private
11300     beginEdit : function(){
11301         this.editing = true;
11302         this.modified = {}; 
11303     },
11304
11305     // private
11306     cancelEdit : function(){
11307         this.editing = false;
11308         delete this.modified;
11309     },
11310
11311     // private
11312     endEdit : function(){
11313         this.editing = false;
11314         if(this.dirty && this.store){
11315             this.store.afterEdit(this);
11316         }
11317     },
11318
11319     /**
11320      * Usually called by the {@link Roo.data.Store} which owns the Record.
11321      * Rejects all changes made to the Record since either creation, or the last commit operation.
11322      * Modified fields are reverted to their original values.
11323      * <p>
11324      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11325      * of reject operations.
11326      */
11327     reject : function(){
11328         var m = this.modified;
11329         for(var n in m){
11330             if(typeof m[n] != "function"){
11331                 this.data[n] = m[n];
11332             }
11333         }
11334         this.dirty = false;
11335         delete this.modified;
11336         this.editing = false;
11337         if(this.store){
11338             this.store.afterReject(this);
11339         }
11340     },
11341
11342     /**
11343      * Usually called by the {@link Roo.data.Store} which owns the Record.
11344      * Commits all changes made to the Record since either creation, or the last commit operation.
11345      * <p>
11346      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11347      * of commit operations.
11348      */
11349     commit : function(){
11350         this.dirty = false;
11351         delete this.modified;
11352         this.editing = false;
11353         if(this.store){
11354             this.store.afterCommit(this);
11355         }
11356     },
11357
11358     // private
11359     hasError : function(){
11360         return this.error != null;
11361     },
11362
11363     // private
11364     clearError : function(){
11365         this.error = null;
11366     },
11367
11368     /**
11369      * Creates a copy of this record.
11370      * @param {String} id (optional) A new record id if you don't want to use this record's id
11371      * @return {Record}
11372      */
11373     copy : function(newId) {
11374         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11375     }
11376 };/*
11377  * Based on:
11378  * Ext JS Library 1.1.1
11379  * Copyright(c) 2006-2007, Ext JS, LLC.
11380  *
11381  * Originally Released Under LGPL - original licence link has changed is not relivant.
11382  *
11383  * Fork - LGPL
11384  * <script type="text/javascript">
11385  */
11386
11387
11388
11389 /**
11390  * @class Roo.data.Store
11391  * @extends Roo.util.Observable
11392  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11393  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11394  * <p>
11395  * 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
11396  * has no knowledge of the format of the data returned by the Proxy.<br>
11397  * <p>
11398  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11399  * instances from the data object. These records are cached and made available through accessor functions.
11400  * @constructor
11401  * Creates a new Store.
11402  * @param {Object} config A config object containing the objects needed for the Store to access data,
11403  * and read the data into Records.
11404  */
11405 Roo.data.Store = function(config){
11406     this.data = new Roo.util.MixedCollection(false);
11407     this.data.getKey = function(o){
11408         return o.id;
11409     };
11410     this.baseParams = {};
11411     // private
11412     this.paramNames = {
11413         "start" : "start",
11414         "limit" : "limit",
11415         "sort" : "sort",
11416         "dir" : "dir",
11417         "multisort" : "_multisort"
11418     };
11419
11420     if(config && config.data){
11421         this.inlineData = config.data;
11422         delete config.data;
11423     }
11424
11425     Roo.apply(this, config);
11426     
11427     if(this.reader){ // reader passed
11428         this.reader = Roo.factory(this.reader, Roo.data);
11429         this.reader.xmodule = this.xmodule || false;
11430         if(!this.recordType){
11431             this.recordType = this.reader.recordType;
11432         }
11433         if(this.reader.onMetaChange){
11434             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11435         }
11436     }
11437
11438     if(this.recordType){
11439         this.fields = this.recordType.prototype.fields;
11440     }
11441     this.modified = [];
11442
11443     this.addEvents({
11444         /**
11445          * @event datachanged
11446          * Fires when the data cache has changed, and a widget which is using this Store
11447          * as a Record cache should refresh its view.
11448          * @param {Store} this
11449          */
11450         datachanged : true,
11451         /**
11452          * @event metachange
11453          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11454          * @param {Store} this
11455          * @param {Object} meta The JSON metadata
11456          */
11457         metachange : true,
11458         /**
11459          * @event add
11460          * Fires when Records have been added to the Store
11461          * @param {Store} this
11462          * @param {Roo.data.Record[]} records The array of Records added
11463          * @param {Number} index The index at which the record(s) were added
11464          */
11465         add : true,
11466         /**
11467          * @event remove
11468          * Fires when a Record has been removed from the Store
11469          * @param {Store} this
11470          * @param {Roo.data.Record} record The Record that was removed
11471          * @param {Number} index The index at which the record was removed
11472          */
11473         remove : true,
11474         /**
11475          * @event update
11476          * Fires when a Record has been updated
11477          * @param {Store} this
11478          * @param {Roo.data.Record} record The Record that was updated
11479          * @param {String} operation The update operation being performed.  Value may be one of:
11480          * <pre><code>
11481  Roo.data.Record.EDIT
11482  Roo.data.Record.REJECT
11483  Roo.data.Record.COMMIT
11484          * </code></pre>
11485          */
11486         update : true,
11487         /**
11488          * @event clear
11489          * Fires when the data cache has been cleared.
11490          * @param {Store} this
11491          */
11492         clear : true,
11493         /**
11494          * @event beforeload
11495          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11496          * the load action will be canceled.
11497          * @param {Store} this
11498          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11499          */
11500         beforeload : true,
11501         /**
11502          * @event beforeloadadd
11503          * Fires after a new set of Records has been loaded.
11504          * @param {Store} this
11505          * @param {Roo.data.Record[]} records The Records that were loaded
11506          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11507          */
11508         beforeloadadd : true,
11509         /**
11510          * @event load
11511          * Fires after a new set of Records has been loaded, before they are added to the store.
11512          * @param {Store} this
11513          * @param {Roo.data.Record[]} records The Records that were loaded
11514          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11515          * @params {Object} return from reader
11516          */
11517         load : true,
11518         /**
11519          * @event loadexception
11520          * Fires if an exception occurs in the Proxy during loading.
11521          * Called with the signature of the Proxy's "loadexception" event.
11522          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11523          * 
11524          * @param {Proxy} 
11525          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11526          * @param {Object} load options 
11527          * @param {Object} jsonData from your request (normally this contains the Exception)
11528          */
11529         loadexception : true
11530     });
11531     
11532     if(this.proxy){
11533         this.proxy = Roo.factory(this.proxy, Roo.data);
11534         this.proxy.xmodule = this.xmodule || false;
11535         this.relayEvents(this.proxy,  ["loadexception"]);
11536     }
11537     this.sortToggle = {};
11538     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11539
11540     Roo.data.Store.superclass.constructor.call(this);
11541
11542     if(this.inlineData){
11543         this.loadData(this.inlineData);
11544         delete this.inlineData;
11545     }
11546 };
11547
11548 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11549      /**
11550     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11551     * without a remote query - used by combo/forms at present.
11552     */
11553     
11554     /**
11555     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11556     */
11557     /**
11558     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11559     */
11560     /**
11561     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11562     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11563     */
11564     /**
11565     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11566     * on any HTTP request
11567     */
11568     /**
11569     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11570     */
11571     /**
11572     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11573     */
11574     multiSort: false,
11575     /**
11576     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11577     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11578     */
11579     remoteSort : false,
11580
11581     /**
11582     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11583      * loaded or when a record is removed. (defaults to false).
11584     */
11585     pruneModifiedRecords : false,
11586
11587     // private
11588     lastOptions : null,
11589
11590     /**
11591      * Add Records to the Store and fires the add event.
11592      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11593      */
11594     add : function(records){
11595         records = [].concat(records);
11596         for(var i = 0, len = records.length; i < len; i++){
11597             records[i].join(this);
11598         }
11599         var index = this.data.length;
11600         this.data.addAll(records);
11601         this.fireEvent("add", this, records, index);
11602     },
11603
11604     /**
11605      * Remove a Record from the Store and fires the remove event.
11606      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11607      */
11608     remove : function(record){
11609         var index = this.data.indexOf(record);
11610         this.data.removeAt(index);
11611  
11612         if(this.pruneModifiedRecords){
11613             this.modified.remove(record);
11614         }
11615         this.fireEvent("remove", this, record, index);
11616     },
11617
11618     /**
11619      * Remove all Records from the Store and fires the clear event.
11620      */
11621     removeAll : function(){
11622         this.data.clear();
11623         if(this.pruneModifiedRecords){
11624             this.modified = [];
11625         }
11626         this.fireEvent("clear", this);
11627     },
11628
11629     /**
11630      * Inserts Records to the Store at the given index and fires the add event.
11631      * @param {Number} index The start index at which to insert the passed Records.
11632      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11633      */
11634     insert : function(index, records){
11635         records = [].concat(records);
11636         for(var i = 0, len = records.length; i < len; i++){
11637             this.data.insert(index, records[i]);
11638             records[i].join(this);
11639         }
11640         this.fireEvent("add", this, records, index);
11641     },
11642
11643     /**
11644      * Get the index within the cache of the passed Record.
11645      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11646      * @return {Number} The index of the passed Record. Returns -1 if not found.
11647      */
11648     indexOf : function(record){
11649         return this.data.indexOf(record);
11650     },
11651
11652     /**
11653      * Get the index within the cache of the Record with the passed id.
11654      * @param {String} id The id of the Record to find.
11655      * @return {Number} The index of the Record. Returns -1 if not found.
11656      */
11657     indexOfId : function(id){
11658         return this.data.indexOfKey(id);
11659     },
11660
11661     /**
11662      * Get the Record with the specified id.
11663      * @param {String} id The id of the Record to find.
11664      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11665      */
11666     getById : function(id){
11667         return this.data.key(id);
11668     },
11669
11670     /**
11671      * Get the Record at the specified index.
11672      * @param {Number} index The index of the Record to find.
11673      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11674      */
11675     getAt : function(index){
11676         return this.data.itemAt(index);
11677     },
11678
11679     /**
11680      * Returns a range of Records between specified indices.
11681      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11682      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11683      * @return {Roo.data.Record[]} An array of Records
11684      */
11685     getRange : function(start, end){
11686         return this.data.getRange(start, end);
11687     },
11688
11689     // private
11690     storeOptions : function(o){
11691         o = Roo.apply({}, o);
11692         delete o.callback;
11693         delete o.scope;
11694         this.lastOptions = o;
11695     },
11696
11697     /**
11698      * Loads the Record cache from the configured Proxy using the configured Reader.
11699      * <p>
11700      * If using remote paging, then the first load call must specify the <em>start</em>
11701      * and <em>limit</em> properties in the options.params property to establish the initial
11702      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11703      * <p>
11704      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11705      * and this call will return before the new data has been loaded. Perform any post-processing
11706      * in a callback function, or in a "load" event handler.</strong>
11707      * <p>
11708      * @param {Object} options An object containing properties which control loading options:<ul>
11709      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11710      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11711      * passed the following arguments:<ul>
11712      * <li>r : Roo.data.Record[]</li>
11713      * <li>options: Options object from the load call</li>
11714      * <li>success: Boolean success indicator</li></ul></li>
11715      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11716      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11717      * </ul>
11718      */
11719     load : function(options){
11720         options = options || {};
11721         if(this.fireEvent("beforeload", this, options) !== false){
11722             this.storeOptions(options);
11723             var p = Roo.apply(options.params || {}, this.baseParams);
11724             // if meta was not loaded from remote source.. try requesting it.
11725             if (!this.reader.metaFromRemote) {
11726                 p._requestMeta = 1;
11727             }
11728             if(this.sortInfo && this.remoteSort){
11729                 var pn = this.paramNames;
11730                 p[pn["sort"]] = this.sortInfo.field;
11731                 p[pn["dir"]] = this.sortInfo.direction;
11732             }
11733             if (this.multiSort) {
11734                 var pn = this.paramNames;
11735                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11736             }
11737             
11738             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11739         }
11740     },
11741
11742     /**
11743      * Reloads the Record cache from the configured Proxy using the configured Reader and
11744      * the options from the last load operation performed.
11745      * @param {Object} options (optional) An object containing properties which may override the options
11746      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11747      * the most recently used options are reused).
11748      */
11749     reload : function(options){
11750         this.load(Roo.applyIf(options||{}, this.lastOptions));
11751     },
11752
11753     // private
11754     // Called as a callback by the Reader during a load operation.
11755     loadRecords : function(o, options, success){
11756         if(!o || success === false){
11757             if(success !== false){
11758                 this.fireEvent("load", this, [], options, o);
11759             }
11760             if(options.callback){
11761                 options.callback.call(options.scope || this, [], options, false);
11762             }
11763             return;
11764         }
11765         // if data returned failure - throw an exception.
11766         if (o.success === false) {
11767             // show a message if no listener is registered.
11768             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11769                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11770             }
11771             // loadmask wil be hooked into this..
11772             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11773             return;
11774         }
11775         var r = o.records, t = o.totalRecords || r.length;
11776         
11777         this.fireEvent("beforeloadadd", this, r, options, o);
11778         
11779         if(!options || options.add !== true){
11780             if(this.pruneModifiedRecords){
11781                 this.modified = [];
11782             }
11783             for(var i = 0, len = r.length; i < len; i++){
11784                 r[i].join(this);
11785             }
11786             if(this.snapshot){
11787                 this.data = this.snapshot;
11788                 delete this.snapshot;
11789             }
11790             this.data.clear();
11791             this.data.addAll(r);
11792             this.totalLength = t;
11793             this.applySort();
11794             this.fireEvent("datachanged", this);
11795         }else{
11796             this.totalLength = Math.max(t, this.data.length+r.length);
11797             this.add(r);
11798         }
11799         
11800         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11801                 
11802             var e = new Roo.data.Record({});
11803
11804             e.set(this.parent.displayField, this.parent.emptyTitle);
11805             e.set(this.parent.valueField, '');
11806
11807             this.insert(0, e);
11808         }
11809             
11810         this.fireEvent("load", this, r, options, o);
11811         if(options.callback){
11812             options.callback.call(options.scope || this, r, options, true);
11813         }
11814     },
11815
11816
11817     /**
11818      * Loads data from a passed data block. A Reader which understands the format of the data
11819      * must have been configured in the constructor.
11820      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11821      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11822      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11823      */
11824     loadData : function(o, append){
11825         var r = this.reader.readRecords(o);
11826         this.loadRecords(r, {add: append}, true);
11827     },
11828
11829     /**
11830      * Gets the number of cached records.
11831      * <p>
11832      * <em>If using paging, this may not be the total size of the dataset. If the data object
11833      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11834      * the data set size</em>
11835      */
11836     getCount : function(){
11837         return this.data.length || 0;
11838     },
11839
11840     /**
11841      * Gets the total number of records in the dataset as returned by the server.
11842      * <p>
11843      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11844      * the dataset size</em>
11845      */
11846     getTotalCount : function(){
11847         return this.totalLength || 0;
11848     },
11849
11850     /**
11851      * Returns the sort state of the Store as an object with two properties:
11852      * <pre><code>
11853  field {String} The name of the field by which the Records are sorted
11854  direction {String} The sort order, "ASC" or "DESC"
11855      * </code></pre>
11856      */
11857     getSortState : function(){
11858         return this.sortInfo;
11859     },
11860
11861     // private
11862     applySort : function(){
11863         if(this.sortInfo && !this.remoteSort){
11864             var s = this.sortInfo, f = s.field;
11865             var st = this.fields.get(f).sortType;
11866             var fn = function(r1, r2){
11867                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11868                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11869             };
11870             this.data.sort(s.direction, fn);
11871             if(this.snapshot && this.snapshot != this.data){
11872                 this.snapshot.sort(s.direction, fn);
11873             }
11874         }
11875     },
11876
11877     /**
11878      * Sets the default sort column and order to be used by the next load operation.
11879      * @param {String} fieldName The name of the field to sort by.
11880      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11881      */
11882     setDefaultSort : function(field, dir){
11883         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11884     },
11885
11886     /**
11887      * Sort the Records.
11888      * If remote sorting is used, the sort is performed on the server, and the cache is
11889      * reloaded. If local sorting is used, the cache is sorted internally.
11890      * @param {String} fieldName The name of the field to sort by.
11891      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11892      */
11893     sort : function(fieldName, dir){
11894         var f = this.fields.get(fieldName);
11895         if(!dir){
11896             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11897             
11898             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11899                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11900             }else{
11901                 dir = f.sortDir;
11902             }
11903         }
11904         this.sortToggle[f.name] = dir;
11905         this.sortInfo = {field: f.name, direction: dir};
11906         if(!this.remoteSort){
11907             this.applySort();
11908             this.fireEvent("datachanged", this);
11909         }else{
11910             this.load(this.lastOptions);
11911         }
11912     },
11913
11914     /**
11915      * Calls the specified function for each of the Records in the cache.
11916      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11917      * Returning <em>false</em> aborts and exits the iteration.
11918      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11919      */
11920     each : function(fn, scope){
11921         this.data.each(fn, scope);
11922     },
11923
11924     /**
11925      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11926      * (e.g., during paging).
11927      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11928      */
11929     getModifiedRecords : function(){
11930         return this.modified;
11931     },
11932
11933     // private
11934     createFilterFn : function(property, value, anyMatch){
11935         if(!value.exec){ // not a regex
11936             value = String(value);
11937             if(value.length == 0){
11938                 return false;
11939             }
11940             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11941         }
11942         return function(r){
11943             return value.test(r.data[property]);
11944         };
11945     },
11946
11947     /**
11948      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11949      * @param {String} property A field on your records
11950      * @param {Number} start The record index to start at (defaults to 0)
11951      * @param {Number} end The last record index to include (defaults to length - 1)
11952      * @return {Number} The sum
11953      */
11954     sum : function(property, start, end){
11955         var rs = this.data.items, v = 0;
11956         start = start || 0;
11957         end = (end || end === 0) ? end : rs.length-1;
11958
11959         for(var i = start; i <= end; i++){
11960             v += (rs[i].data[property] || 0);
11961         }
11962         return v;
11963     },
11964
11965     /**
11966      * Filter 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      */
11972     filter : function(property, value, anyMatch){
11973         var fn = this.createFilterFn(property, value, anyMatch);
11974         return fn ? this.filterBy(fn) : this.clearFilter();
11975     },
11976
11977     /**
11978      * Filter by a function. The specified function will be called with each
11979      * record in this data source. If the function returns true the record is included,
11980      * otherwise it is filtered.
11981      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11982      * @param {Object} scope (optional) The scope of the function (defaults to this)
11983      */
11984     filterBy : function(fn, scope){
11985         this.snapshot = this.snapshot || this.data;
11986         this.data = this.queryBy(fn, scope||this);
11987         this.fireEvent("datachanged", this);
11988     },
11989
11990     /**
11991      * Query the records by a specified property.
11992      * @param {String} field A field on your records
11993      * @param {String/RegExp} value Either a string that the field
11994      * should start with or a RegExp to test against the field
11995      * @param {Boolean} anyMatch True to match any part not just the beginning
11996      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11997      */
11998     query : function(property, value, anyMatch){
11999         var fn = this.createFilterFn(property, value, anyMatch);
12000         return fn ? this.queryBy(fn) : this.data.clone();
12001     },
12002
12003     /**
12004      * Query by a function. The specified function will be called with each
12005      * record in this data source. If the function returns true the record is included
12006      * in the results.
12007      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
12008      * @param {Object} scope (optional) The scope of the function (defaults to this)
12009       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
12010      **/
12011     queryBy : function(fn, scope){
12012         var data = this.snapshot || this.data;
12013         return data.filterBy(fn, scope||this);
12014     },
12015
12016     /**
12017      * Collects unique values for a particular dataIndex from this store.
12018      * @param {String} dataIndex The property to collect
12019      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
12020      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
12021      * @return {Array} An array of the unique values
12022      **/
12023     collect : function(dataIndex, allowNull, bypassFilter){
12024         var d = (bypassFilter === true && this.snapshot) ?
12025                 this.snapshot.items : this.data.items;
12026         var v, sv, r = [], l = {};
12027         for(var i = 0, len = d.length; i < len; i++){
12028             v = d[i].data[dataIndex];
12029             sv = String(v);
12030             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12031                 l[sv] = true;
12032                 r[r.length] = v;
12033             }
12034         }
12035         return r;
12036     },
12037
12038     /**
12039      * Revert to a view of the Record cache with no filtering applied.
12040      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12041      */
12042     clearFilter : function(suppressEvent){
12043         if(this.snapshot && this.snapshot != this.data){
12044             this.data = this.snapshot;
12045             delete this.snapshot;
12046             if(suppressEvent !== true){
12047                 this.fireEvent("datachanged", this);
12048             }
12049         }
12050     },
12051
12052     // private
12053     afterEdit : function(record){
12054         if(this.modified.indexOf(record) == -1){
12055             this.modified.push(record);
12056         }
12057         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12058     },
12059     
12060     // private
12061     afterReject : function(record){
12062         this.modified.remove(record);
12063         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12064     },
12065
12066     // private
12067     afterCommit : function(record){
12068         this.modified.remove(record);
12069         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12070     },
12071
12072     /**
12073      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12074      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12075      */
12076     commitChanges : function(){
12077         var m = this.modified.slice(0);
12078         this.modified = [];
12079         for(var i = 0, len = m.length; i < len; i++){
12080             m[i].commit();
12081         }
12082     },
12083
12084     /**
12085      * Cancel outstanding changes on all changed records.
12086      */
12087     rejectChanges : function(){
12088         var m = this.modified.slice(0);
12089         this.modified = [];
12090         for(var i = 0, len = m.length; i < len; i++){
12091             m[i].reject();
12092         }
12093     },
12094
12095     onMetaChange : function(meta, rtype, o){
12096         this.recordType = rtype;
12097         this.fields = rtype.prototype.fields;
12098         delete this.snapshot;
12099         this.sortInfo = meta.sortInfo || this.sortInfo;
12100         this.modified = [];
12101         this.fireEvent('metachange', this, this.reader.meta);
12102     },
12103     
12104     moveIndex : function(data, type)
12105     {
12106         var index = this.indexOf(data);
12107         
12108         var newIndex = index + type;
12109         
12110         this.remove(data);
12111         
12112         this.insert(newIndex, data);
12113         
12114     }
12115 });/*
12116  * Based on:
12117  * Ext JS Library 1.1.1
12118  * Copyright(c) 2006-2007, Ext JS, LLC.
12119  *
12120  * Originally Released Under LGPL - original licence link has changed is not relivant.
12121  *
12122  * Fork - LGPL
12123  * <script type="text/javascript">
12124  */
12125
12126 /**
12127  * @class Roo.data.SimpleStore
12128  * @extends Roo.data.Store
12129  * Small helper class to make creating Stores from Array data easier.
12130  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12131  * @cfg {Array} fields An array of field definition objects, or field name strings.
12132  * @cfg {Object} an existing reader (eg. copied from another store)
12133  * @cfg {Array} data The multi-dimensional array of data
12134  * @constructor
12135  * @param {Object} config
12136  */
12137 Roo.data.SimpleStore = function(config)
12138 {
12139     Roo.data.SimpleStore.superclass.constructor.call(this, {
12140         isLocal : true,
12141         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
12142                 id: config.id
12143             },
12144             Roo.data.Record.create(config.fields)
12145         ),
12146         proxy : new Roo.data.MemoryProxy(config.data)
12147     });
12148     this.load();
12149 };
12150 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12151  * Based on:
12152  * Ext JS Library 1.1.1
12153  * Copyright(c) 2006-2007, Ext JS, LLC.
12154  *
12155  * Originally Released Under LGPL - original licence link has changed is not relivant.
12156  *
12157  * Fork - LGPL
12158  * <script type="text/javascript">
12159  */
12160
12161 /**
12162 /**
12163  * @extends Roo.data.Store
12164  * @class Roo.data.JsonStore
12165  * Small helper class to make creating Stores for JSON data easier. <br/>
12166 <pre><code>
12167 var store = new Roo.data.JsonStore({
12168     url: 'get-images.php',
12169     root: 'images',
12170     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12171 });
12172 </code></pre>
12173  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12174  * JsonReader and HttpProxy (unless inline data is provided).</b>
12175  * @cfg {Array} fields An array of field definition objects, or field name strings.
12176  * @constructor
12177  * @param {Object} config
12178  */
12179 Roo.data.JsonStore = function(c){
12180     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12181         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12182         reader: new Roo.data.JsonReader(c, c.fields)
12183     }));
12184 };
12185 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12186  * Based on:
12187  * Ext JS Library 1.1.1
12188  * Copyright(c) 2006-2007, Ext JS, LLC.
12189  *
12190  * Originally Released Under LGPL - original licence link has changed is not relivant.
12191  *
12192  * Fork - LGPL
12193  * <script type="text/javascript">
12194  */
12195
12196  
12197 Roo.data.Field = function(config){
12198     if(typeof config == "string"){
12199         config = {name: config};
12200     }
12201     Roo.apply(this, config);
12202     
12203     if(!this.type){
12204         this.type = "auto";
12205     }
12206     
12207     var st = Roo.data.SortTypes;
12208     // named sortTypes are supported, here we look them up
12209     if(typeof this.sortType == "string"){
12210         this.sortType = st[this.sortType];
12211     }
12212     
12213     // set default sortType for strings and dates
12214     if(!this.sortType){
12215         switch(this.type){
12216             case "string":
12217                 this.sortType = st.asUCString;
12218                 break;
12219             case "date":
12220                 this.sortType = st.asDate;
12221                 break;
12222             default:
12223                 this.sortType = st.none;
12224         }
12225     }
12226
12227     // define once
12228     var stripRe = /[\$,%]/g;
12229
12230     // prebuilt conversion function for this field, instead of
12231     // switching every time we're reading a value
12232     if(!this.convert){
12233         var cv, dateFormat = this.dateFormat;
12234         switch(this.type){
12235             case "":
12236             case "auto":
12237             case undefined:
12238                 cv = function(v){ return v; };
12239                 break;
12240             case "string":
12241                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12242                 break;
12243             case "int":
12244                 cv = function(v){
12245                     return v !== undefined && v !== null && v !== '' ?
12246                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12247                     };
12248                 break;
12249             case "float":
12250                 cv = function(v){
12251                     return v !== undefined && v !== null && v !== '' ?
12252                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12253                     };
12254                 break;
12255             case "bool":
12256             case "boolean":
12257                 cv = function(v){ return v === true || v === "true" || v == 1; };
12258                 break;
12259             case "date":
12260                 cv = function(v){
12261                     if(!v){
12262                         return '';
12263                     }
12264                     if(v instanceof Date){
12265                         return v;
12266                     }
12267                     if(dateFormat){
12268                         if(dateFormat == "timestamp"){
12269                             return new Date(v*1000);
12270                         }
12271                         return Date.parseDate(v, dateFormat);
12272                     }
12273                     var parsed = Date.parse(v);
12274                     return parsed ? new Date(parsed) : null;
12275                 };
12276              break;
12277             
12278         }
12279         this.convert = cv;
12280     }
12281 };
12282
12283 Roo.data.Field.prototype = {
12284     dateFormat: null,
12285     defaultValue: "",
12286     mapping: null,
12287     sortType : null,
12288     sortDir : "ASC"
12289 };/*
12290  * Based on:
12291  * Ext JS Library 1.1.1
12292  * Copyright(c) 2006-2007, Ext JS, LLC.
12293  *
12294  * Originally Released Under LGPL - original licence link has changed is not relivant.
12295  *
12296  * Fork - LGPL
12297  * <script type="text/javascript">
12298  */
12299  
12300 // Base class for reading structured data from a data source.  This class is intended to be
12301 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12302
12303 /**
12304  * @class Roo.data.DataReader
12305  * Base class for reading structured data from a data source.  This class is intended to be
12306  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12307  */
12308
12309 Roo.data.DataReader = function(meta, recordType){
12310     
12311     this.meta = meta;
12312     
12313     this.recordType = recordType instanceof Array ? 
12314         Roo.data.Record.create(recordType) : recordType;
12315 };
12316
12317 Roo.data.DataReader.prototype = {
12318     
12319     
12320     readerType : 'Data',
12321      /**
12322      * Create an empty record
12323      * @param {Object} data (optional) - overlay some values
12324      * @return {Roo.data.Record} record created.
12325      */
12326     newRow :  function(d) {
12327         var da =  {};
12328         this.recordType.prototype.fields.each(function(c) {
12329             switch( c.type) {
12330                 case 'int' : da[c.name] = 0; break;
12331                 case 'date' : da[c.name] = new Date(); break;
12332                 case 'float' : da[c.name] = 0.0; break;
12333                 case 'boolean' : da[c.name] = false; break;
12334                 default : da[c.name] = ""; break;
12335             }
12336             
12337         });
12338         return new this.recordType(Roo.apply(da, d));
12339     }
12340     
12341     
12342 };/*
12343  * Based on:
12344  * Ext JS Library 1.1.1
12345  * Copyright(c) 2006-2007, Ext JS, LLC.
12346  *
12347  * Originally Released Under LGPL - original licence link has changed is not relivant.
12348  *
12349  * Fork - LGPL
12350  * <script type="text/javascript">
12351  */
12352
12353 /**
12354  * @class Roo.data.DataProxy
12355  * @extends Roo.data.Observable
12356  * This class is an abstract base class for implementations which provide retrieval of
12357  * unformatted data objects.<br>
12358  * <p>
12359  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12360  * (of the appropriate type which knows how to parse the data object) to provide a block of
12361  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12362  * <p>
12363  * Custom implementations must implement the load method as described in
12364  * {@link Roo.data.HttpProxy#load}.
12365  */
12366 Roo.data.DataProxy = function(){
12367     this.addEvents({
12368         /**
12369          * @event beforeload
12370          * Fires before a network request is made to retrieve a data object.
12371          * @param {Object} This DataProxy object.
12372          * @param {Object} params The params parameter to the load function.
12373          */
12374         beforeload : true,
12375         /**
12376          * @event load
12377          * Fires before the load method's callback is called.
12378          * @param {Object} This DataProxy object.
12379          * @param {Object} o The data object.
12380          * @param {Object} arg The callback argument object passed to the load function.
12381          */
12382         load : true,
12383         /**
12384          * @event loadexception
12385          * Fires if an Exception occurs during data retrieval.
12386          * @param {Object} This DataProxy object.
12387          * @param {Object} o The data object.
12388          * @param {Object} arg The callback argument object passed to the load function.
12389          * @param {Object} e The Exception.
12390          */
12391         loadexception : true
12392     });
12393     Roo.data.DataProxy.superclass.constructor.call(this);
12394 };
12395
12396 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12397
12398     /**
12399      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12400      */
12401 /*
12402  * Based on:
12403  * Ext JS Library 1.1.1
12404  * Copyright(c) 2006-2007, Ext JS, LLC.
12405  *
12406  * Originally Released Under LGPL - original licence link has changed is not relivant.
12407  *
12408  * Fork - LGPL
12409  * <script type="text/javascript">
12410  */
12411 /**
12412  * @class Roo.data.MemoryProxy
12413  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12414  * to the Reader when its load method is called.
12415  * @constructor
12416  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12417  */
12418 Roo.data.MemoryProxy = function(data){
12419     if (data.data) {
12420         data = data.data;
12421     }
12422     Roo.data.MemoryProxy.superclass.constructor.call(this);
12423     this.data = data;
12424 };
12425
12426 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12427     
12428     /**
12429      * Load data from the requested source (in this case an in-memory
12430      * data object passed to the constructor), read the data object into
12431      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12432      * process that block using the passed callback.
12433      * @param {Object} params This parameter is not used by the MemoryProxy class.
12434      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12435      * object into a block of Roo.data.Records.
12436      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12437      * The function must be passed <ul>
12438      * <li>The Record block object</li>
12439      * <li>The "arg" argument from the load function</li>
12440      * <li>A boolean success indicator</li>
12441      * </ul>
12442      * @param {Object} scope The scope in which to call the callback
12443      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12444      */
12445     load : function(params, reader, callback, scope, arg){
12446         params = params || {};
12447         var result;
12448         try {
12449             result = reader.readRecords(params.data ? params.data :this.data);
12450         }catch(e){
12451             this.fireEvent("loadexception", this, arg, null, e);
12452             callback.call(scope, null, arg, false);
12453             return;
12454         }
12455         callback.call(scope, result, arg, true);
12456     },
12457     
12458     // private
12459     update : function(params, records){
12460         
12461     }
12462 });/*
12463  * Based on:
12464  * Ext JS Library 1.1.1
12465  * Copyright(c) 2006-2007, Ext JS, LLC.
12466  *
12467  * Originally Released Under LGPL - original licence link has changed is not relivant.
12468  *
12469  * Fork - LGPL
12470  * <script type="text/javascript">
12471  */
12472 /**
12473  * @class Roo.data.HttpProxy
12474  * @extends Roo.data.DataProxy
12475  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12476  * configured to reference a certain URL.<br><br>
12477  * <p>
12478  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12479  * from which the running page was served.<br><br>
12480  * <p>
12481  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12482  * <p>
12483  * Be aware that to enable the browser to parse an XML document, the server must set
12484  * the Content-Type header in the HTTP response to "text/xml".
12485  * @constructor
12486  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12487  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12488  * will be used to make the request.
12489  */
12490 Roo.data.HttpProxy = function(conn){
12491     Roo.data.HttpProxy.superclass.constructor.call(this);
12492     // is conn a conn config or a real conn?
12493     this.conn = conn;
12494     this.useAjax = !conn || !conn.events;
12495   
12496 };
12497
12498 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12499     // thse are take from connection...
12500     
12501     /**
12502      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12503      */
12504     /**
12505      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12506      * extra parameters to each request made by this object. (defaults to undefined)
12507      */
12508     /**
12509      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12510      *  to each request made by this object. (defaults to undefined)
12511      */
12512     /**
12513      * @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)
12514      */
12515     /**
12516      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12517      */
12518      /**
12519      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12520      * @type Boolean
12521      */
12522   
12523
12524     /**
12525      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12526      * @type Boolean
12527      */
12528     /**
12529      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12530      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12531      * a finer-grained basis than the DataProxy events.
12532      */
12533     getConnection : function(){
12534         return this.useAjax ? Roo.Ajax : this.conn;
12535     },
12536
12537     /**
12538      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12539      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12540      * process that block using the passed callback.
12541      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12542      * for the request to the remote server.
12543      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12544      * object into a block of Roo.data.Records.
12545      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12546      * The function must be passed <ul>
12547      * <li>The Record block object</li>
12548      * <li>The "arg" argument from the load function</li>
12549      * <li>A boolean success indicator</li>
12550      * </ul>
12551      * @param {Object} scope The scope in which to call the callback
12552      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12553      */
12554     load : function(params, reader, callback, scope, arg){
12555         if(this.fireEvent("beforeload", this, params) !== false){
12556             var  o = {
12557                 params : params || {},
12558                 request: {
12559                     callback : callback,
12560                     scope : scope,
12561                     arg : arg
12562                 },
12563                 reader: reader,
12564                 callback : this.loadResponse,
12565                 scope: this
12566             };
12567             if(this.useAjax){
12568                 Roo.applyIf(o, this.conn);
12569                 if(this.activeRequest){
12570                     Roo.Ajax.abort(this.activeRequest);
12571                 }
12572                 this.activeRequest = Roo.Ajax.request(o);
12573             }else{
12574                 this.conn.request(o);
12575             }
12576         }else{
12577             callback.call(scope||this, null, arg, false);
12578         }
12579     },
12580
12581     // private
12582     loadResponse : function(o, success, response){
12583         delete this.activeRequest;
12584         if(!success){
12585             this.fireEvent("loadexception", this, o, response);
12586             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12587             return;
12588         }
12589         var result;
12590         try {
12591             result = o.reader.read(response);
12592         }catch(e){
12593             this.fireEvent("loadexception", this, o, response, e);
12594             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12595             return;
12596         }
12597         
12598         this.fireEvent("load", this, o, o.request.arg);
12599         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12600     },
12601
12602     // private
12603     update : function(dataSet){
12604
12605     },
12606
12607     // private
12608     updateResponse : function(dataSet){
12609
12610     }
12611 });/*
12612  * Based on:
12613  * Ext JS Library 1.1.1
12614  * Copyright(c) 2006-2007, Ext JS, LLC.
12615  *
12616  * Originally Released Under LGPL - original licence link has changed is not relivant.
12617  *
12618  * Fork - LGPL
12619  * <script type="text/javascript">
12620  */
12621
12622 /**
12623  * @class Roo.data.ScriptTagProxy
12624  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12625  * other than the originating domain of the running page.<br><br>
12626  * <p>
12627  * <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
12628  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12629  * <p>
12630  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12631  * source code that is used as the source inside a &lt;script> tag.<br><br>
12632  * <p>
12633  * In order for the browser to process the returned data, the server must wrap the data object
12634  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12635  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12636  * depending on whether the callback name was passed:
12637  * <p>
12638  * <pre><code>
12639 boolean scriptTag = false;
12640 String cb = request.getParameter("callback");
12641 if (cb != null) {
12642     scriptTag = true;
12643     response.setContentType("text/javascript");
12644 } else {
12645     response.setContentType("application/x-json");
12646 }
12647 Writer out = response.getWriter();
12648 if (scriptTag) {
12649     out.write(cb + "(");
12650 }
12651 out.print(dataBlock.toJsonString());
12652 if (scriptTag) {
12653     out.write(");");
12654 }
12655 </pre></code>
12656  *
12657  * @constructor
12658  * @param {Object} config A configuration object.
12659  */
12660 Roo.data.ScriptTagProxy = function(config){
12661     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12662     Roo.apply(this, config);
12663     this.head = document.getElementsByTagName("head")[0];
12664 };
12665
12666 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12667
12668 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12669     /**
12670      * @cfg {String} url The URL from which to request the data object.
12671      */
12672     /**
12673      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12674      */
12675     timeout : 30000,
12676     /**
12677      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12678      * the server the name of the callback function set up by the load call to process the returned data object.
12679      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12680      * javascript output which calls this named function passing the data object as its only parameter.
12681      */
12682     callbackParam : "callback",
12683     /**
12684      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12685      * name to the request.
12686      */
12687     nocache : true,
12688
12689     /**
12690      * Load data from the configured URL, read the data object into
12691      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12692      * process that block using the passed callback.
12693      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12694      * for the request to the remote server.
12695      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12696      * object into a block of Roo.data.Records.
12697      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12698      * The function must be passed <ul>
12699      * <li>The Record block object</li>
12700      * <li>The "arg" argument from the load function</li>
12701      * <li>A boolean success indicator</li>
12702      * </ul>
12703      * @param {Object} scope The scope in which to call the callback
12704      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12705      */
12706     load : function(params, reader, callback, scope, arg){
12707         if(this.fireEvent("beforeload", this, params) !== false){
12708
12709             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12710
12711             var url = this.url;
12712             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12713             if(this.nocache){
12714                 url += "&_dc=" + (new Date().getTime());
12715             }
12716             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12717             var trans = {
12718                 id : transId,
12719                 cb : "stcCallback"+transId,
12720                 scriptId : "stcScript"+transId,
12721                 params : params,
12722                 arg : arg,
12723                 url : url,
12724                 callback : callback,
12725                 scope : scope,
12726                 reader : reader
12727             };
12728             var conn = this;
12729
12730             window[trans.cb] = function(o){
12731                 conn.handleResponse(o, trans);
12732             };
12733
12734             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12735
12736             if(this.autoAbort !== false){
12737                 this.abort();
12738             }
12739
12740             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12741
12742             var script = document.createElement("script");
12743             script.setAttribute("src", url);
12744             script.setAttribute("type", "text/javascript");
12745             script.setAttribute("id", trans.scriptId);
12746             this.head.appendChild(script);
12747
12748             this.trans = trans;
12749         }else{
12750             callback.call(scope||this, null, arg, false);
12751         }
12752     },
12753
12754     // private
12755     isLoading : function(){
12756         return this.trans ? true : false;
12757     },
12758
12759     /**
12760      * Abort the current server request.
12761      */
12762     abort : function(){
12763         if(this.isLoading()){
12764             this.destroyTrans(this.trans);
12765         }
12766     },
12767
12768     // private
12769     destroyTrans : function(trans, isLoaded){
12770         this.head.removeChild(document.getElementById(trans.scriptId));
12771         clearTimeout(trans.timeoutId);
12772         if(isLoaded){
12773             window[trans.cb] = undefined;
12774             try{
12775                 delete window[trans.cb];
12776             }catch(e){}
12777         }else{
12778             // if hasn't been loaded, wait for load to remove it to prevent script error
12779             window[trans.cb] = function(){
12780                 window[trans.cb] = undefined;
12781                 try{
12782                     delete window[trans.cb];
12783                 }catch(e){}
12784             };
12785         }
12786     },
12787
12788     // private
12789     handleResponse : function(o, trans){
12790         this.trans = false;
12791         this.destroyTrans(trans, true);
12792         var result;
12793         try {
12794             result = trans.reader.readRecords(o);
12795         }catch(e){
12796             this.fireEvent("loadexception", this, o, trans.arg, e);
12797             trans.callback.call(trans.scope||window, null, trans.arg, false);
12798             return;
12799         }
12800         this.fireEvent("load", this, o, trans.arg);
12801         trans.callback.call(trans.scope||window, result, trans.arg, true);
12802     },
12803
12804     // private
12805     handleFailure : function(trans){
12806         this.trans = false;
12807         this.destroyTrans(trans, false);
12808         this.fireEvent("loadexception", this, null, trans.arg);
12809         trans.callback.call(trans.scope||window, null, trans.arg, false);
12810     }
12811 });/*
12812  * Based on:
12813  * Ext JS Library 1.1.1
12814  * Copyright(c) 2006-2007, Ext JS, LLC.
12815  *
12816  * Originally Released Under LGPL - original licence link has changed is not relivant.
12817  *
12818  * Fork - LGPL
12819  * <script type="text/javascript">
12820  */
12821
12822 /**
12823  * @class Roo.data.JsonReader
12824  * @extends Roo.data.DataReader
12825  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12826  * based on mappings in a provided Roo.data.Record constructor.
12827  * 
12828  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12829  * in the reply previously. 
12830  * 
12831  * <p>
12832  * Example code:
12833  * <pre><code>
12834 var RecordDef = Roo.data.Record.create([
12835     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12836     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12837 ]);
12838 var myReader = new Roo.data.JsonReader({
12839     totalProperty: "results",    // The property which contains the total dataset size (optional)
12840     root: "rows",                // The property which contains an Array of row objects
12841     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12842 }, RecordDef);
12843 </code></pre>
12844  * <p>
12845  * This would consume a JSON file like this:
12846  * <pre><code>
12847 { 'results': 2, 'rows': [
12848     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12849     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12850 }
12851 </code></pre>
12852  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12853  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12854  * paged from the remote server.
12855  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12856  * @cfg {String} root name of the property which contains the Array of row objects.
12857  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12858  * @cfg {Array} fields Array of field definition objects
12859  * @constructor
12860  * Create a new JsonReader
12861  * @param {Object} meta Metadata configuration options
12862  * @param {Object} recordType Either an Array of field definition objects,
12863  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12864  */
12865 Roo.data.JsonReader = function(meta, recordType){
12866     
12867     meta = meta || {};
12868     // set some defaults:
12869     Roo.applyIf(meta, {
12870         totalProperty: 'total',
12871         successProperty : 'success',
12872         root : 'data',
12873         id : 'id'
12874     });
12875     
12876     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12877 };
12878 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12879     
12880     readerType : 'Json',
12881     
12882     /**
12883      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12884      * Used by Store query builder to append _requestMeta to params.
12885      * 
12886      */
12887     metaFromRemote : false,
12888     /**
12889      * This method is only used by a DataProxy which has retrieved data from a remote server.
12890      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12891      * @return {Object} data A data block which is used by an Roo.data.Store object as
12892      * a cache of Roo.data.Records.
12893      */
12894     read : function(response){
12895         var json = response.responseText;
12896        
12897         var o = /* eval:var:o */ eval("("+json+")");
12898         if(!o) {
12899             throw {message: "JsonReader.read: Json object not found"};
12900         }
12901         
12902         if(o.metaData){
12903             
12904             delete this.ef;
12905             this.metaFromRemote = true;
12906             this.meta = o.metaData;
12907             this.recordType = Roo.data.Record.create(o.metaData.fields);
12908             this.onMetaChange(this.meta, this.recordType, o);
12909         }
12910         return this.readRecords(o);
12911     },
12912
12913     // private function a store will implement
12914     onMetaChange : function(meta, recordType, o){
12915
12916     },
12917
12918     /**
12919          * @ignore
12920          */
12921     simpleAccess: function(obj, subsc) {
12922         return obj[subsc];
12923     },
12924
12925         /**
12926          * @ignore
12927          */
12928     getJsonAccessor: function(){
12929         var re = /[\[\.]/;
12930         return function(expr) {
12931             try {
12932                 return(re.test(expr))
12933                     ? new Function("obj", "return obj." + expr)
12934                     : function(obj){
12935                         return obj[expr];
12936                     };
12937             } catch(e){}
12938             return Roo.emptyFn;
12939         };
12940     }(),
12941
12942     /**
12943      * Create a data block containing Roo.data.Records from an XML document.
12944      * @param {Object} o An object which contains an Array of row objects in the property specified
12945      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12946      * which contains the total size of the dataset.
12947      * @return {Object} data A data block which is used by an Roo.data.Store object as
12948      * a cache of Roo.data.Records.
12949      */
12950     readRecords : function(o){
12951         /**
12952          * After any data loads, the raw JSON data is available for further custom processing.
12953          * @type Object
12954          */
12955         this.o = o;
12956         var s = this.meta, Record = this.recordType,
12957             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12958
12959 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12960         if (!this.ef) {
12961             if(s.totalProperty) {
12962                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12963                 }
12964                 if(s.successProperty) {
12965                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12966                 }
12967                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12968                 if (s.id) {
12969                         var g = this.getJsonAccessor(s.id);
12970                         this.getId = function(rec) {
12971                                 var r = g(rec);  
12972                                 return (r === undefined || r === "") ? null : r;
12973                         };
12974                 } else {
12975                         this.getId = function(){return null;};
12976                 }
12977             this.ef = [];
12978             for(var jj = 0; jj < fl; jj++){
12979                 f = fi[jj];
12980                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12981                 this.ef[jj] = this.getJsonAccessor(map);
12982             }
12983         }
12984
12985         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12986         if(s.totalProperty){
12987             var vt = parseInt(this.getTotal(o), 10);
12988             if(!isNaN(vt)){
12989                 totalRecords = vt;
12990             }
12991         }
12992         if(s.successProperty){
12993             var vs = this.getSuccess(o);
12994             if(vs === false || vs === 'false'){
12995                 success = false;
12996             }
12997         }
12998         var records = [];
12999         for(var i = 0; i < c; i++){
13000                 var n = root[i];
13001             var values = {};
13002             var id = this.getId(n);
13003             for(var j = 0; j < fl; j++){
13004                 f = fi[j];
13005             var v = this.ef[j](n);
13006             if (!f.convert) {
13007                 Roo.log('missing convert for ' + f.name);
13008                 Roo.log(f);
13009                 continue;
13010             }
13011             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13012             }
13013             var record = new Record(values, id);
13014             record.json = n;
13015             records[i] = record;
13016         }
13017         return {
13018             raw : o,
13019             success : success,
13020             records : records,
13021             totalRecords : totalRecords
13022         };
13023     }
13024 });/*
13025  * Based on:
13026  * Ext JS Library 1.1.1
13027  * Copyright(c) 2006-2007, Ext JS, LLC.
13028  *
13029  * Originally Released Under LGPL - original licence link has changed is not relivant.
13030  *
13031  * Fork - LGPL
13032  * <script type="text/javascript">
13033  */
13034
13035 /**
13036  * @class Roo.data.ArrayReader
13037  * @extends Roo.data.DataReader
13038  * Data reader class to create an Array of Roo.data.Record objects from an Array.
13039  * Each element of that Array represents a row of data fields. The
13040  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13041  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13042  * <p>
13043  * Example code:.
13044  * <pre><code>
13045 var RecordDef = Roo.data.Record.create([
13046     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
13047     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
13048 ]);
13049 var myReader = new Roo.data.ArrayReader({
13050     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
13051 }, RecordDef);
13052 </code></pre>
13053  * <p>
13054  * This would consume an Array like this:
13055  * <pre><code>
13056 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13057   </code></pre>
13058  
13059  * @constructor
13060  * Create a new JsonReader
13061  * @param {Object} meta Metadata configuration options.
13062  * @param {Object|Array} recordType Either an Array of field definition objects
13063  * 
13064  * @cfg {Array} fields Array of field definition objects
13065  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13066  * as specified to {@link Roo.data.Record#create},
13067  * or an {@link Roo.data.Record} object
13068  *
13069  * 
13070  * created using {@link Roo.data.Record#create}.
13071  */
13072 Roo.data.ArrayReader = function(meta, recordType)
13073 {    
13074     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13075 };
13076
13077 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13078     
13079     readerType : 'Array',
13080     /**
13081      * Create a data block containing Roo.data.Records from an XML document.
13082      * @param {Object} o An Array of row objects which represents the dataset.
13083      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13084      * a cache of Roo.data.Records.
13085      */
13086     readRecords : function(o)
13087     {
13088         var sid = this.meta ? this.meta.id : null;
13089         var recordType = this.recordType, fields = recordType.prototype.fields;
13090         var records = [];
13091         var root = o;
13092         for(var i = 0; i < root.length; i++){
13093                 var n = root[i];
13094             var values = {};
13095             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13096             for(var j = 0, jlen = fields.length; j < jlen; j++){
13097                 var f = fields.items[j];
13098                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13099                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13100                 v = f.convert(v);
13101                 values[f.name] = v;
13102             }
13103             var record = new recordType(values, id);
13104             record.json = n;
13105             records[records.length] = record;
13106         }
13107         return {
13108             records : records,
13109             totalRecords : records.length
13110         };
13111     }
13112 });/*
13113  * - LGPL
13114  * * 
13115  */
13116
13117 /**
13118  * @class Roo.bootstrap.ComboBox
13119  * @extends Roo.bootstrap.TriggerField
13120  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13121  * @cfg {Boolean} append (true|false) default false
13122  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13123  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13124  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13125  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13126  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13127  * @cfg {Boolean} animate default true
13128  * @cfg {Boolean} emptyResultText only for touch device
13129  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13130  * @cfg {String} emptyTitle default ''
13131  * @constructor
13132  * Create a new ComboBox.
13133  * @param {Object} config Configuration options
13134  */
13135 Roo.bootstrap.ComboBox = function(config){
13136     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13137     this.addEvents({
13138         /**
13139          * @event expand
13140          * Fires when the dropdown list is expanded
13141         * @param {Roo.bootstrap.ComboBox} combo This combo box
13142         */
13143         'expand' : true,
13144         /**
13145          * @event collapse
13146          * Fires when the dropdown list is collapsed
13147         * @param {Roo.bootstrap.ComboBox} combo This combo box
13148         */
13149         'collapse' : true,
13150         /**
13151          * @event beforeselect
13152          * Fires before a list item is selected. Return false to cancel the selection.
13153         * @param {Roo.bootstrap.ComboBox} combo This combo box
13154         * @param {Roo.data.Record} record The data record returned from the underlying store
13155         * @param {Number} index The index of the selected item in the dropdown list
13156         */
13157         'beforeselect' : true,
13158         /**
13159          * @event select
13160          * Fires when a list item is selected
13161         * @param {Roo.bootstrap.ComboBox} combo This combo box
13162         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13163         * @param {Number} index The index of the selected item in the dropdown list
13164         */
13165         'select' : true,
13166         /**
13167          * @event beforequery
13168          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13169          * The event object passed has these properties:
13170         * @param {Roo.bootstrap.ComboBox} combo This combo box
13171         * @param {String} query The query
13172         * @param {Boolean} forceAll true to force "all" query
13173         * @param {Boolean} cancel true to cancel the query
13174         * @param {Object} e The query event object
13175         */
13176         'beforequery': true,
13177          /**
13178          * @event add
13179          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13180         * @param {Roo.bootstrap.ComboBox} combo This combo box
13181         */
13182         'add' : true,
13183         /**
13184          * @event edit
13185          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13186         * @param {Roo.bootstrap.ComboBox} combo This combo box
13187         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13188         */
13189         'edit' : true,
13190         /**
13191          * @event remove
13192          * Fires when the remove value from the combobox array
13193         * @param {Roo.bootstrap.ComboBox} combo This combo box
13194         */
13195         'remove' : true,
13196         /**
13197          * @event afterremove
13198          * Fires when the remove value from the combobox array
13199         * @param {Roo.bootstrap.ComboBox} combo This combo box
13200         */
13201         'afterremove' : true,
13202         /**
13203          * @event specialfilter
13204          * Fires when specialfilter
13205             * @param {Roo.bootstrap.ComboBox} combo This combo box
13206             */
13207         'specialfilter' : true,
13208         /**
13209          * @event tick
13210          * Fires when tick the element
13211             * @param {Roo.bootstrap.ComboBox} combo This combo box
13212             */
13213         'tick' : true,
13214         /**
13215          * @event touchviewdisplay
13216          * Fires when touch view require special display (default is using displayField)
13217             * @param {Roo.bootstrap.ComboBox} combo This combo box
13218             * @param {Object} cfg set html .
13219             */
13220         'touchviewdisplay' : true
13221         
13222     });
13223     
13224     this.item = [];
13225     this.tickItems = [];
13226     
13227     this.selectedIndex = -1;
13228     if(this.mode == 'local'){
13229         if(config.queryDelay === undefined){
13230             this.queryDelay = 10;
13231         }
13232         if(config.minChars === undefined){
13233             this.minChars = 0;
13234         }
13235     }
13236 };
13237
13238 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13239      
13240     /**
13241      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13242      * rendering into an Roo.Editor, defaults to false)
13243      */
13244     /**
13245      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13246      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13247      */
13248     /**
13249      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13250      */
13251     /**
13252      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13253      * the dropdown list (defaults to undefined, with no header element)
13254      */
13255
13256      /**
13257      * @cfg {String/Roo.Template} tpl The template to use to render the output
13258      */
13259      
13260      /**
13261      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13262      */
13263     listWidth: undefined,
13264     /**
13265      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13266      * mode = 'remote' or 'text' if mode = 'local')
13267      */
13268     displayField: undefined,
13269     
13270     /**
13271      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13272      * mode = 'remote' or 'value' if mode = 'local'). 
13273      * Note: use of a valueField requires the user make a selection
13274      * in order for a value to be mapped.
13275      */
13276     valueField: undefined,
13277     /**
13278      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13279      */
13280     modalTitle : '',
13281     
13282     /**
13283      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13284      * field's data value (defaults to the underlying DOM element's name)
13285      */
13286     hiddenName: undefined,
13287     /**
13288      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13289      */
13290     listClass: '',
13291     /**
13292      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13293      */
13294     selectedClass: 'active',
13295     
13296     /**
13297      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13298      */
13299     shadow:'sides',
13300     /**
13301      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13302      * anchor positions (defaults to 'tl-bl')
13303      */
13304     listAlign: 'tl-bl?',
13305     /**
13306      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13307      */
13308     maxHeight: 300,
13309     /**
13310      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13311      * query specified by the allQuery config option (defaults to 'query')
13312      */
13313     triggerAction: 'query',
13314     /**
13315      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13316      * (defaults to 4, does not apply if editable = false)
13317      */
13318     minChars : 4,
13319     /**
13320      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13321      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13322      */
13323     typeAhead: false,
13324     /**
13325      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13326      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13327      */
13328     queryDelay: 500,
13329     /**
13330      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13331      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13332      */
13333     pageSize: 0,
13334     /**
13335      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13336      * when editable = true (defaults to false)
13337      */
13338     selectOnFocus:false,
13339     /**
13340      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13341      */
13342     queryParam: 'query',
13343     /**
13344      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13345      * when mode = 'remote' (defaults to 'Loading...')
13346      */
13347     loadingText: 'Loading...',
13348     /**
13349      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13350      */
13351     resizable: false,
13352     /**
13353      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13354      */
13355     handleHeight : 8,
13356     /**
13357      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13358      * traditional select (defaults to true)
13359      */
13360     editable: true,
13361     /**
13362      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13363      */
13364     allQuery: '',
13365     /**
13366      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13367      */
13368     mode: 'remote',
13369     /**
13370      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13371      * listWidth has a higher value)
13372      */
13373     minListWidth : 70,
13374     /**
13375      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13376      * allow the user to set arbitrary text into the field (defaults to false)
13377      */
13378     forceSelection:false,
13379     /**
13380      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13381      * if typeAhead = true (defaults to 250)
13382      */
13383     typeAheadDelay : 250,
13384     /**
13385      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13386      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13387      */
13388     valueNotFoundText : undefined,
13389     /**
13390      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13391      */
13392     blockFocus : false,
13393     
13394     /**
13395      * @cfg {Boolean} disableClear Disable showing of clear button.
13396      */
13397     disableClear : false,
13398     /**
13399      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13400      */
13401     alwaysQuery : false,
13402     
13403     /**
13404      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13405      */
13406     multiple : false,
13407     
13408     /**
13409      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13410      */
13411     invalidClass : "has-warning",
13412     
13413     /**
13414      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13415      */
13416     validClass : "has-success",
13417     
13418     /**
13419      * @cfg {Boolean} specialFilter (true|false) special filter default false
13420      */
13421     specialFilter : false,
13422     
13423     /**
13424      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13425      */
13426     mobileTouchView : true,
13427     
13428     /**
13429      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13430      */
13431     useNativeIOS : false,
13432     
13433     /**
13434      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13435      */
13436     mobile_restrict_height : false,
13437     
13438     ios_options : false,
13439     
13440     //private
13441     addicon : false,
13442     editicon: false,
13443     
13444     page: 0,
13445     hasQuery: false,
13446     append: false,
13447     loadNext: false,
13448     autoFocus : true,
13449     tickable : false,
13450     btnPosition : 'right',
13451     triggerList : true,
13452     showToggleBtn : true,
13453     animate : true,
13454     emptyResultText: 'Empty',
13455     triggerText : 'Select',
13456     emptyTitle : '',
13457     
13458     // element that contains real text value.. (when hidden is used..)
13459     
13460     getAutoCreate : function()
13461     {   
13462         var cfg = false;
13463         //render
13464         /*
13465          * Render classic select for iso
13466          */
13467         
13468         if(Roo.isIOS && this.useNativeIOS){
13469             cfg = this.getAutoCreateNativeIOS();
13470             return cfg;
13471         }
13472         
13473         /*
13474          * Touch Devices
13475          */
13476         
13477         if(Roo.isTouch && this.mobileTouchView){
13478             cfg = this.getAutoCreateTouchView();
13479             return cfg;;
13480         }
13481         
13482         /*
13483          *  Normal ComboBox
13484          */
13485         if(!this.tickable){
13486             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13487             return cfg;
13488         }
13489         
13490         /*
13491          *  ComboBox with tickable selections
13492          */
13493              
13494         var align = this.labelAlign || this.parentLabelAlign();
13495         
13496         cfg = {
13497             cls : 'form-group roo-combobox-tickable' //input-group
13498         };
13499         
13500         var btn_text_select = '';
13501         var btn_text_done = '';
13502         var btn_text_cancel = '';
13503         
13504         if (this.btn_text_show) {
13505             btn_text_select = 'Select';
13506             btn_text_done = 'Done';
13507             btn_text_cancel = 'Cancel'; 
13508         }
13509         
13510         var buttons = {
13511             tag : 'div',
13512             cls : 'tickable-buttons',
13513             cn : [
13514                 {
13515                     tag : 'button',
13516                     type : 'button',
13517                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13518                     //html : this.triggerText
13519                     html: btn_text_select
13520                 },
13521                 {
13522                     tag : 'button',
13523                     type : 'button',
13524                     name : 'ok',
13525                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13526                     //html : 'Done'
13527                     html: btn_text_done
13528                 },
13529                 {
13530                     tag : 'button',
13531                     type : 'button',
13532                     name : 'cancel',
13533                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13534                     //html : 'Cancel'
13535                     html: btn_text_cancel
13536                 }
13537             ]
13538         };
13539         
13540         if(this.editable){
13541             buttons.cn.unshift({
13542                 tag: 'input',
13543                 cls: 'roo-select2-search-field-input'
13544             });
13545         }
13546         
13547         var _this = this;
13548         
13549         Roo.each(buttons.cn, function(c){
13550             if (_this.size) {
13551                 c.cls += ' btn-' + _this.size;
13552             }
13553
13554             if (_this.disabled) {
13555                 c.disabled = true;
13556             }
13557         });
13558         
13559         var box = {
13560             tag: 'div',
13561             style : 'display: contents',
13562             cn: [
13563                 {
13564                     tag: 'input',
13565                     type : 'hidden',
13566                     cls: 'form-hidden-field'
13567                 },
13568                 {
13569                     tag: 'ul',
13570                     cls: 'roo-select2-choices',
13571                     cn:[
13572                         {
13573                             tag: 'li',
13574                             cls: 'roo-select2-search-field',
13575                             cn: [
13576                                 buttons
13577                             ]
13578                         }
13579                     ]
13580                 }
13581             ]
13582         };
13583         
13584         var combobox = {
13585             cls: 'roo-select2-container input-group roo-select2-container-multi',
13586             cn: [
13587                 
13588                 box
13589 //                {
13590 //                    tag: 'ul',
13591 //                    cls: 'typeahead typeahead-long dropdown-menu',
13592 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13593 //                }
13594             ]
13595         };
13596         
13597         if(this.hasFeedback && !this.allowBlank){
13598             
13599             var feedback = {
13600                 tag: 'span',
13601                 cls: 'glyphicon form-control-feedback'
13602             };
13603
13604             combobox.cn.push(feedback);
13605         }
13606         
13607         var indicator = {
13608             tag : 'i',
13609             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13610             tooltip : 'This field is required'
13611         };
13612         if (Roo.bootstrap.version == 4) {
13613             indicator = {
13614                 tag : 'i',
13615                 style : 'display:none'
13616             };
13617         }
13618         if (align ==='left' && this.fieldLabel.length) {
13619             
13620             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13621             
13622             cfg.cn = [
13623                 indicator,
13624                 {
13625                     tag: 'label',
13626                     'for' :  id,
13627                     cls : 'control-label col-form-label',
13628                     html : this.fieldLabel
13629
13630                 },
13631                 {
13632                     cls : "", 
13633                     cn: [
13634                         combobox
13635                     ]
13636                 }
13637
13638             ];
13639             
13640             var labelCfg = cfg.cn[1];
13641             var contentCfg = cfg.cn[2];
13642             
13643
13644             if(this.indicatorpos == 'right'){
13645                 
13646                 cfg.cn = [
13647                     {
13648                         tag: 'label',
13649                         'for' :  id,
13650                         cls : 'control-label col-form-label',
13651                         cn : [
13652                             {
13653                                 tag : 'span',
13654                                 html : this.fieldLabel
13655                             },
13656                             indicator
13657                         ]
13658                     },
13659                     {
13660                         cls : "",
13661                         cn: [
13662                             combobox
13663                         ]
13664                     }
13665
13666                 ];
13667                 
13668                 
13669                 
13670                 labelCfg = cfg.cn[0];
13671                 contentCfg = cfg.cn[1];
13672             
13673             }
13674             
13675             if(this.labelWidth > 12){
13676                 labelCfg.style = "width: " + this.labelWidth + 'px';
13677             }
13678             
13679             if(this.labelWidth < 13 && this.labelmd == 0){
13680                 this.labelmd = this.labelWidth;
13681             }
13682             
13683             if(this.labellg > 0){
13684                 labelCfg.cls += ' col-lg-' + this.labellg;
13685                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13686             }
13687             
13688             if(this.labelmd > 0){
13689                 labelCfg.cls += ' col-md-' + this.labelmd;
13690                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13691             }
13692             
13693             if(this.labelsm > 0){
13694                 labelCfg.cls += ' col-sm-' + this.labelsm;
13695                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13696             }
13697             
13698             if(this.labelxs > 0){
13699                 labelCfg.cls += ' col-xs-' + this.labelxs;
13700                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13701             }
13702                 
13703                 
13704         } else if ( this.fieldLabel.length) {
13705 //                Roo.log(" label");
13706                  cfg.cn = [
13707                    indicator,
13708                     {
13709                         tag: 'label',
13710                         //cls : 'input-group-addon',
13711                         html : this.fieldLabel
13712                     },
13713                     combobox
13714                 ];
13715                 
13716                 if(this.indicatorpos == 'right'){
13717                     cfg.cn = [
13718                         {
13719                             tag: 'label',
13720                             //cls : 'input-group-addon',
13721                             html : this.fieldLabel
13722                         },
13723                         indicator,
13724                         combobox
13725                     ];
13726                     
13727                 }
13728
13729         } else {
13730             
13731 //                Roo.log(" no label && no align");
13732                 cfg = combobox
13733                      
13734                 
13735         }
13736          
13737         var settings=this;
13738         ['xs','sm','md','lg'].map(function(size){
13739             if (settings[size]) {
13740                 cfg.cls += ' col-' + size + '-' + settings[size];
13741             }
13742         });
13743         
13744         return cfg;
13745         
13746     },
13747     
13748     _initEventsCalled : false,
13749     
13750     // private
13751     initEvents: function()
13752     {   
13753         if (this._initEventsCalled) { // as we call render... prevent looping...
13754             return;
13755         }
13756         this._initEventsCalled = true;
13757         
13758         if (!this.store) {
13759             throw "can not find store for combo";
13760         }
13761         
13762         this.indicator = this.indicatorEl();
13763         
13764         this.store = Roo.factory(this.store, Roo.data);
13765         this.store.parent = this;
13766         
13767         // if we are building from html. then this element is so complex, that we can not really
13768         // use the rendered HTML.
13769         // so we have to trash and replace the previous code.
13770         if (Roo.XComponent.build_from_html) {
13771             // remove this element....
13772             var e = this.el.dom, k=0;
13773             while (e ) { e = e.previousSibling;  ++k;}
13774
13775             this.el.remove();
13776             
13777             this.el=false;
13778             this.rendered = false;
13779             
13780             this.render(this.parent().getChildContainer(true), k);
13781         }
13782         
13783         if(Roo.isIOS && this.useNativeIOS){
13784             this.initIOSView();
13785             return;
13786         }
13787         
13788         /*
13789          * Touch Devices
13790          */
13791         
13792         if(Roo.isTouch && this.mobileTouchView){
13793             this.initTouchView();
13794             return;
13795         }
13796         
13797         if(this.tickable){
13798             this.initTickableEvents();
13799             return;
13800         }
13801         
13802         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13803         
13804         if(this.hiddenName){
13805             
13806             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13807             
13808             this.hiddenField.dom.value =
13809                 this.hiddenValue !== undefined ? this.hiddenValue :
13810                 this.value !== undefined ? this.value : '';
13811
13812             // prevent input submission
13813             this.el.dom.removeAttribute('name');
13814             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13815              
13816              
13817         }
13818         //if(Roo.isGecko){
13819         //    this.el.dom.setAttribute('autocomplete', 'off');
13820         //}
13821         
13822         var cls = 'x-combo-list';
13823         
13824         //this.list = new Roo.Layer({
13825         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13826         //});
13827         
13828         var _this = this;
13829         
13830         (function(){
13831             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13832             _this.list.setWidth(lw);
13833         }).defer(100);
13834         
13835         this.list.on('mouseover', this.onViewOver, this);
13836         this.list.on('mousemove', this.onViewMove, this);
13837         this.list.on('scroll', this.onViewScroll, this);
13838         
13839         /*
13840         this.list.swallowEvent('mousewheel');
13841         this.assetHeight = 0;
13842
13843         if(this.title){
13844             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13845             this.assetHeight += this.header.getHeight();
13846         }
13847
13848         this.innerList = this.list.createChild({cls:cls+'-inner'});
13849         this.innerList.on('mouseover', this.onViewOver, this);
13850         this.innerList.on('mousemove', this.onViewMove, this);
13851         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13852         
13853         if(this.allowBlank && !this.pageSize && !this.disableClear){
13854             this.footer = this.list.createChild({cls:cls+'-ft'});
13855             this.pageTb = new Roo.Toolbar(this.footer);
13856            
13857         }
13858         if(this.pageSize){
13859             this.footer = this.list.createChild({cls:cls+'-ft'});
13860             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13861                     {pageSize: this.pageSize});
13862             
13863         }
13864         
13865         if (this.pageTb && this.allowBlank && !this.disableClear) {
13866             var _this = this;
13867             this.pageTb.add(new Roo.Toolbar.Fill(), {
13868                 cls: 'x-btn-icon x-btn-clear',
13869                 text: '&#160;',
13870                 handler: function()
13871                 {
13872                     _this.collapse();
13873                     _this.clearValue();
13874                     _this.onSelect(false, -1);
13875                 }
13876             });
13877         }
13878         if (this.footer) {
13879             this.assetHeight += this.footer.getHeight();
13880         }
13881         */
13882             
13883         if(!this.tpl){
13884             this.tpl = Roo.bootstrap.version == 4 ?
13885                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13886                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13887         }
13888
13889         this.view = new Roo.View(this.list, this.tpl, {
13890             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13891         });
13892         //this.view.wrapEl.setDisplayed(false);
13893         this.view.on('click', this.onViewClick, this);
13894         
13895         
13896         this.store.on('beforeload', this.onBeforeLoad, this);
13897         this.store.on('load', this.onLoad, this);
13898         this.store.on('loadexception', this.onLoadException, this);
13899         /*
13900         if(this.resizable){
13901             this.resizer = new Roo.Resizable(this.list,  {
13902                pinned:true, handles:'se'
13903             });
13904             this.resizer.on('resize', function(r, w, h){
13905                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13906                 this.listWidth = w;
13907                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13908                 this.restrictHeight();
13909             }, this);
13910             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13911         }
13912         */
13913         if(!this.editable){
13914             this.editable = true;
13915             this.setEditable(false);
13916         }
13917         
13918         /*
13919         
13920         if (typeof(this.events.add.listeners) != 'undefined') {
13921             
13922             this.addicon = this.wrap.createChild(
13923                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13924        
13925             this.addicon.on('click', function(e) {
13926                 this.fireEvent('add', this);
13927             }, this);
13928         }
13929         if (typeof(this.events.edit.listeners) != 'undefined') {
13930             
13931             this.editicon = this.wrap.createChild(
13932                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13933             if (this.addicon) {
13934                 this.editicon.setStyle('margin-left', '40px');
13935             }
13936             this.editicon.on('click', function(e) {
13937                 
13938                 // we fire even  if inothing is selected..
13939                 this.fireEvent('edit', this, this.lastData );
13940                 
13941             }, this);
13942         }
13943         */
13944         
13945         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13946             "up" : function(e){
13947                 this.inKeyMode = true;
13948                 this.selectPrev();
13949             },
13950
13951             "down" : function(e){
13952                 if(!this.isExpanded()){
13953                     this.onTriggerClick();
13954                 }else{
13955                     this.inKeyMode = true;
13956                     this.selectNext();
13957                 }
13958             },
13959
13960             "enter" : function(e){
13961 //                this.onViewClick();
13962                 //return true;
13963                 this.collapse();
13964                 
13965                 if(this.fireEvent("specialkey", this, e)){
13966                     this.onViewClick(false);
13967                 }
13968                 
13969                 return true;
13970             },
13971
13972             "esc" : function(e){
13973                 this.collapse();
13974             },
13975
13976             "tab" : function(e){
13977                 this.collapse();
13978                 
13979                 if(this.fireEvent("specialkey", this, e)){
13980                     this.onViewClick(false);
13981                 }
13982                 
13983                 return true;
13984             },
13985
13986             scope : this,
13987
13988             doRelay : function(foo, bar, hname){
13989                 if(hname == 'down' || this.scope.isExpanded()){
13990                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13991                 }
13992                 return true;
13993             },
13994
13995             forceKeyDown: true
13996         });
13997         
13998         
13999         this.queryDelay = Math.max(this.queryDelay || 10,
14000                 this.mode == 'local' ? 10 : 250);
14001         
14002         
14003         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14004         
14005         if(this.typeAhead){
14006             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14007         }
14008         if(this.editable !== false){
14009             this.inputEl().on("keyup", this.onKeyUp, this);
14010         }
14011         if(this.forceSelection){
14012             this.inputEl().on('blur', this.doForce, this);
14013         }
14014         
14015         if(this.multiple){
14016             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14017             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14018         }
14019     },
14020     
14021     initTickableEvents: function()
14022     {   
14023         this.createList();
14024         
14025         if(this.hiddenName){
14026             
14027             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14028             
14029             this.hiddenField.dom.value =
14030                 this.hiddenValue !== undefined ? this.hiddenValue :
14031                 this.value !== undefined ? this.value : '';
14032
14033             // prevent input submission
14034             this.el.dom.removeAttribute('name');
14035             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14036              
14037              
14038         }
14039         
14040 //        this.list = this.el.select('ul.dropdown-menu',true).first();
14041         
14042         this.choices = this.el.select('ul.roo-select2-choices', true).first();
14043         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14044         if(this.triggerList){
14045             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14046         }
14047          
14048         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14049         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14050         
14051         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14052         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14053         
14054         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14055         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14056         
14057         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14058         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14059         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14060         
14061         this.okBtn.hide();
14062         this.cancelBtn.hide();
14063         
14064         var _this = this;
14065         
14066         (function(){
14067             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14068             _this.list.setWidth(lw);
14069         }).defer(100);
14070         
14071         this.list.on('mouseover', this.onViewOver, this);
14072         this.list.on('mousemove', this.onViewMove, this);
14073         
14074         this.list.on('scroll', this.onViewScroll, this);
14075         
14076         if(!this.tpl){
14077             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
14078                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14079         }
14080
14081         this.view = new Roo.View(this.list, this.tpl, {
14082             singleSelect:true,
14083             tickable:true,
14084             parent:this,
14085             store: this.store,
14086             selectedClass: this.selectedClass
14087         });
14088         
14089         //this.view.wrapEl.setDisplayed(false);
14090         this.view.on('click', this.onViewClick, this);
14091         
14092         
14093         
14094         this.store.on('beforeload', this.onBeforeLoad, this);
14095         this.store.on('load', this.onLoad, this);
14096         this.store.on('loadexception', this.onLoadException, this);
14097         
14098         if(this.editable){
14099             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14100                 "up" : function(e){
14101                     this.inKeyMode = true;
14102                     this.selectPrev();
14103                 },
14104
14105                 "down" : function(e){
14106                     this.inKeyMode = true;
14107                     this.selectNext();
14108                 },
14109
14110                 "enter" : function(e){
14111                     if(this.fireEvent("specialkey", this, e)){
14112                         this.onViewClick(false);
14113                     }
14114                     
14115                     return true;
14116                 },
14117
14118                 "esc" : function(e){
14119                     this.onTickableFooterButtonClick(e, false, false);
14120                 },
14121
14122                 "tab" : function(e){
14123                     this.fireEvent("specialkey", this, e);
14124                     
14125                     this.onTickableFooterButtonClick(e, false, false);
14126                     
14127                     return true;
14128                 },
14129
14130                 scope : this,
14131
14132                 doRelay : function(e, fn, key){
14133                     if(this.scope.isExpanded()){
14134                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14135                     }
14136                     return true;
14137                 },
14138
14139                 forceKeyDown: true
14140             });
14141         }
14142         
14143         this.queryDelay = Math.max(this.queryDelay || 10,
14144                 this.mode == 'local' ? 10 : 250);
14145         
14146         
14147         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14148         
14149         if(this.typeAhead){
14150             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14151         }
14152         
14153         if(this.editable !== false){
14154             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14155         }
14156         
14157         this.indicator = this.indicatorEl();
14158         
14159         if(this.indicator){
14160             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14161             this.indicator.hide();
14162         }
14163         
14164     },
14165
14166     onDestroy : function(){
14167         if(this.view){
14168             this.view.setStore(null);
14169             this.view.el.removeAllListeners();
14170             this.view.el.remove();
14171             this.view.purgeListeners();
14172         }
14173         if(this.list){
14174             this.list.dom.innerHTML  = '';
14175         }
14176         
14177         if(this.store){
14178             this.store.un('beforeload', this.onBeforeLoad, this);
14179             this.store.un('load', this.onLoad, this);
14180             this.store.un('loadexception', this.onLoadException, this);
14181         }
14182         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14183     },
14184
14185     // private
14186     fireKey : function(e){
14187         if(e.isNavKeyPress() && !this.list.isVisible()){
14188             this.fireEvent("specialkey", this, e);
14189         }
14190     },
14191
14192     // private
14193     onResize: function(w, h){
14194 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14195 //        
14196 //        if(typeof w != 'number'){
14197 //            // we do not handle it!?!?
14198 //            return;
14199 //        }
14200 //        var tw = this.trigger.getWidth();
14201 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14202 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14203 //        var x = w - tw;
14204 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14205 //            
14206 //        //this.trigger.setStyle('left', x+'px');
14207 //        
14208 //        if(this.list && this.listWidth === undefined){
14209 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14210 //            this.list.setWidth(lw);
14211 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14212 //        }
14213         
14214     
14215         
14216     },
14217
14218     /**
14219      * Allow or prevent the user from directly editing the field text.  If false is passed,
14220      * the user will only be able to select from the items defined in the dropdown list.  This method
14221      * is the runtime equivalent of setting the 'editable' config option at config time.
14222      * @param {Boolean} value True to allow the user to directly edit the field text
14223      */
14224     setEditable : function(value){
14225         if(value == this.editable){
14226             return;
14227         }
14228         this.editable = value;
14229         if(!value){
14230             this.inputEl().dom.setAttribute('readOnly', true);
14231             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14232             this.inputEl().addClass('x-combo-noedit');
14233         }else{
14234             this.inputEl().dom.setAttribute('readOnly', false);
14235             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14236             this.inputEl().removeClass('x-combo-noedit');
14237         }
14238     },
14239
14240     // private
14241     
14242     onBeforeLoad : function(combo,opts){
14243         if(!this.hasFocus){
14244             return;
14245         }
14246          if (!opts.add) {
14247             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14248          }
14249         this.restrictHeight();
14250         this.selectedIndex = -1;
14251     },
14252
14253     // private
14254     onLoad : function(){
14255         
14256         this.hasQuery = false;
14257         
14258         if(!this.hasFocus){
14259             return;
14260         }
14261         
14262         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14263             this.loading.hide();
14264         }
14265         
14266         if(this.store.getCount() > 0){
14267             
14268             this.expand();
14269             this.restrictHeight();
14270             if(this.lastQuery == this.allQuery){
14271                 if(this.editable && !this.tickable){
14272                     this.inputEl().dom.select();
14273                 }
14274                 
14275                 if(
14276                     !this.selectByValue(this.value, true) &&
14277                     this.autoFocus && 
14278                     (
14279                         !this.store.lastOptions ||
14280                         typeof(this.store.lastOptions.add) == 'undefined' || 
14281                         this.store.lastOptions.add != true
14282                     )
14283                 ){
14284                     this.select(0, true);
14285                 }
14286             }else{
14287                 if(this.autoFocus){
14288                     this.selectNext();
14289                 }
14290                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14291                     this.taTask.delay(this.typeAheadDelay);
14292                 }
14293             }
14294         }else{
14295             this.onEmptyResults();
14296         }
14297         
14298         //this.el.focus();
14299     },
14300     // private
14301     onLoadException : function()
14302     {
14303         this.hasQuery = false;
14304         
14305         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14306             this.loading.hide();
14307         }
14308         
14309         if(this.tickable && this.editable){
14310             return;
14311         }
14312         
14313         this.collapse();
14314         // only causes errors at present
14315         //Roo.log(this.store.reader.jsonData);
14316         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14317             // fixme
14318             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14319         //}
14320         
14321         
14322     },
14323     // private
14324     onTypeAhead : function(){
14325         if(this.store.getCount() > 0){
14326             var r = this.store.getAt(0);
14327             var newValue = r.data[this.displayField];
14328             var len = newValue.length;
14329             var selStart = this.getRawValue().length;
14330             
14331             if(selStart != len){
14332                 this.setRawValue(newValue);
14333                 this.selectText(selStart, newValue.length);
14334             }
14335         }
14336     },
14337
14338     // private
14339     onSelect : function(record, index){
14340         
14341         if(this.fireEvent('beforeselect', this, record, index) !== false){
14342         
14343             this.setFromData(index > -1 ? record.data : false);
14344             
14345             this.collapse();
14346             this.fireEvent('select', this, record, index);
14347         }
14348     },
14349
14350     /**
14351      * Returns the currently selected field value or empty string if no value is set.
14352      * @return {String} value The selected value
14353      */
14354     getValue : function()
14355     {
14356         if(Roo.isIOS && this.useNativeIOS){
14357             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14358         }
14359         
14360         if(this.multiple){
14361             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14362         }
14363         
14364         if(this.valueField){
14365             return typeof this.value != 'undefined' ? this.value : '';
14366         }else{
14367             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14368         }
14369     },
14370     
14371     getRawValue : function()
14372     {
14373         if(Roo.isIOS && this.useNativeIOS){
14374             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14375         }
14376         
14377         var v = this.inputEl().getValue();
14378         
14379         return v;
14380     },
14381
14382     /**
14383      * Clears any text/value currently set in the field
14384      */
14385     clearValue : function(){
14386         
14387         if(this.hiddenField){
14388             this.hiddenField.dom.value = '';
14389         }
14390         this.value = '';
14391         this.setRawValue('');
14392         this.lastSelectionText = '';
14393         this.lastData = false;
14394         
14395         var close = this.closeTriggerEl();
14396         
14397         if(close){
14398             close.hide();
14399         }
14400         
14401         this.validate();
14402         
14403     },
14404
14405     /**
14406      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14407      * will be displayed in the field.  If the value does not match the data value of an existing item,
14408      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14409      * Otherwise the field will be blank (although the value will still be set).
14410      * @param {String} value The value to match
14411      */
14412     setValue : function(v)
14413     {
14414         if(Roo.isIOS && this.useNativeIOS){
14415             this.setIOSValue(v);
14416             return;
14417         }
14418         
14419         if(this.multiple){
14420             this.syncValue();
14421             return;
14422         }
14423         
14424         var text = v;
14425         if(this.valueField){
14426             var r = this.findRecord(this.valueField, v);
14427             if(r){
14428                 text = r.data[this.displayField];
14429             }else if(this.valueNotFoundText !== undefined){
14430                 text = this.valueNotFoundText;
14431             }
14432         }
14433         this.lastSelectionText = text;
14434         if(this.hiddenField){
14435             this.hiddenField.dom.value = v;
14436         }
14437         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14438         this.value = v;
14439         
14440         var close = this.closeTriggerEl();
14441         
14442         if(close){
14443             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14444         }
14445         
14446         this.validate();
14447     },
14448     /**
14449      * @property {Object} the last set data for the element
14450      */
14451     
14452     lastData : false,
14453     /**
14454      * Sets the value of the field based on a object which is related to the record format for the store.
14455      * @param {Object} value the value to set as. or false on reset?
14456      */
14457     setFromData : function(o){
14458         
14459         if(this.multiple){
14460             this.addItem(o);
14461             return;
14462         }
14463             
14464         var dv = ''; // display value
14465         var vv = ''; // value value..
14466         this.lastData = o;
14467         if (this.displayField) {
14468             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14469         } else {
14470             // this is an error condition!!!
14471             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14472         }
14473         
14474         if(this.valueField){
14475             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14476         }
14477         
14478         var close = this.closeTriggerEl();
14479         
14480         if(close){
14481             if(dv.length || vv * 1 > 0){
14482                 close.show() ;
14483                 this.blockFocus=true;
14484             } else {
14485                 close.hide();
14486             }             
14487         }
14488         
14489         if(this.hiddenField){
14490             this.hiddenField.dom.value = vv;
14491             
14492             this.lastSelectionText = dv;
14493             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14494             this.value = vv;
14495             return;
14496         }
14497         // no hidden field.. - we store the value in 'value', but still display
14498         // display field!!!!
14499         this.lastSelectionText = dv;
14500         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14501         this.value = vv;
14502         
14503         
14504         
14505     },
14506     // private
14507     reset : function(){
14508         // overridden so that last data is reset..
14509         
14510         if(this.multiple){
14511             this.clearItem();
14512             return;
14513         }
14514         
14515         this.setValue(this.originalValue);
14516         //this.clearInvalid();
14517         this.lastData = false;
14518         if (this.view) {
14519             this.view.clearSelections();
14520         }
14521         
14522         this.validate();
14523     },
14524     // private
14525     findRecord : function(prop, value){
14526         var record;
14527         if(this.store.getCount() > 0){
14528             this.store.each(function(r){
14529                 if(r.data[prop] == value){
14530                     record = r;
14531                     return false;
14532                 }
14533                 return true;
14534             });
14535         }
14536         return record;
14537     },
14538     
14539     getName: function()
14540     {
14541         // returns hidden if it's set..
14542         if (!this.rendered) {return ''};
14543         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14544         
14545     },
14546     // private
14547     onViewMove : function(e, t){
14548         this.inKeyMode = false;
14549     },
14550
14551     // private
14552     onViewOver : function(e, t){
14553         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14554             return;
14555         }
14556         var item = this.view.findItemFromChild(t);
14557         
14558         if(item){
14559             var index = this.view.indexOf(item);
14560             this.select(index, false);
14561         }
14562     },
14563
14564     // private
14565     onViewClick : function(view, doFocus, el, e)
14566     {
14567         var index = this.view.getSelectedIndexes()[0];
14568         
14569         var r = this.store.getAt(index);
14570         
14571         if(this.tickable){
14572             
14573             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14574                 return;
14575             }
14576             
14577             var rm = false;
14578             var _this = this;
14579             
14580             Roo.each(this.tickItems, function(v,k){
14581                 
14582                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14583                     Roo.log(v);
14584                     _this.tickItems.splice(k, 1);
14585                     
14586                     if(typeof(e) == 'undefined' && view == false){
14587                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14588                     }
14589                     
14590                     rm = true;
14591                     return;
14592                 }
14593             });
14594             
14595             if(rm){
14596                 return;
14597             }
14598             
14599             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14600                 this.tickItems.push(r.data);
14601             }
14602             
14603             if(typeof(e) == 'undefined' && view == false){
14604                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14605             }
14606                     
14607             return;
14608         }
14609         
14610         if(r){
14611             this.onSelect(r, index);
14612         }
14613         if(doFocus !== false && !this.blockFocus){
14614             this.inputEl().focus();
14615         }
14616     },
14617
14618     // private
14619     restrictHeight : function(){
14620         //this.innerList.dom.style.height = '';
14621         //var inner = this.innerList.dom;
14622         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14623         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14624         //this.list.beginUpdate();
14625         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14626         this.list.alignTo(this.inputEl(), this.listAlign);
14627         this.list.alignTo(this.inputEl(), this.listAlign);
14628         //this.list.endUpdate();
14629     },
14630
14631     // private
14632     onEmptyResults : function(){
14633         
14634         if(this.tickable && this.editable){
14635             this.hasFocus = false;
14636             this.restrictHeight();
14637             return;
14638         }
14639         
14640         this.collapse();
14641     },
14642
14643     /**
14644      * Returns true if the dropdown list is expanded, else false.
14645      */
14646     isExpanded : function(){
14647         return this.list.isVisible();
14648     },
14649
14650     /**
14651      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14652      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14653      * @param {String} value The data value of the item to select
14654      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14655      * selected item if it is not currently in view (defaults to true)
14656      * @return {Boolean} True if the value matched an item in the list, else false
14657      */
14658     selectByValue : function(v, scrollIntoView){
14659         if(v !== undefined && v !== null){
14660             var r = this.findRecord(this.valueField || this.displayField, v);
14661             if(r){
14662                 this.select(this.store.indexOf(r), scrollIntoView);
14663                 return true;
14664             }
14665         }
14666         return false;
14667     },
14668
14669     /**
14670      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14671      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14672      * @param {Number} index The zero-based index of the list item to select
14673      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14674      * selected item if it is not currently in view (defaults to true)
14675      */
14676     select : function(index, scrollIntoView){
14677         this.selectedIndex = index;
14678         this.view.select(index);
14679         if(scrollIntoView !== false){
14680             var el = this.view.getNode(index);
14681             /*
14682              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14683              */
14684             if(el){
14685                 this.list.scrollChildIntoView(el, false);
14686             }
14687         }
14688     },
14689
14690     // private
14691     selectNext : function(){
14692         var ct = this.store.getCount();
14693         if(ct > 0){
14694             if(this.selectedIndex == -1){
14695                 this.select(0);
14696             }else if(this.selectedIndex < ct-1){
14697                 this.select(this.selectedIndex+1);
14698             }
14699         }
14700     },
14701
14702     // private
14703     selectPrev : function(){
14704         var ct = this.store.getCount();
14705         if(ct > 0){
14706             if(this.selectedIndex == -1){
14707                 this.select(0);
14708             }else if(this.selectedIndex != 0){
14709                 this.select(this.selectedIndex-1);
14710             }
14711         }
14712     },
14713
14714     // private
14715     onKeyUp : function(e){
14716         if(this.editable !== false && !e.isSpecialKey()){
14717             this.lastKey = e.getKey();
14718             this.dqTask.delay(this.queryDelay);
14719         }
14720     },
14721
14722     // private
14723     validateBlur : function(){
14724         return !this.list || !this.list.isVisible();   
14725     },
14726
14727     // private
14728     initQuery : function(){
14729         
14730         var v = this.getRawValue();
14731         
14732         if(this.tickable && this.editable){
14733             v = this.tickableInputEl().getValue();
14734         }
14735         
14736         this.doQuery(v);
14737     },
14738
14739     // private
14740     doForce : function(){
14741         if(this.inputEl().dom.value.length > 0){
14742             this.inputEl().dom.value =
14743                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14744              
14745         }
14746     },
14747
14748     /**
14749      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14750      * query allowing the query action to be canceled if needed.
14751      * @param {String} query The SQL query to execute
14752      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14753      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14754      * saved in the current store (defaults to false)
14755      */
14756     doQuery : function(q, forceAll){
14757         
14758         if(q === undefined || q === null){
14759             q = '';
14760         }
14761         var qe = {
14762             query: q,
14763             forceAll: forceAll,
14764             combo: this,
14765             cancel:false
14766         };
14767         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14768             return false;
14769         }
14770         q = qe.query;
14771         
14772         forceAll = qe.forceAll;
14773         if(forceAll === true || (q.length >= this.minChars)){
14774             
14775             this.hasQuery = true;
14776             
14777             if(this.lastQuery != q || this.alwaysQuery){
14778                 this.lastQuery = q;
14779                 if(this.mode == 'local'){
14780                     this.selectedIndex = -1;
14781                     if(forceAll){
14782                         this.store.clearFilter();
14783                     }else{
14784                         
14785                         if(this.specialFilter){
14786                             this.fireEvent('specialfilter', this);
14787                             this.onLoad();
14788                             return;
14789                         }
14790                         
14791                         this.store.filter(this.displayField, q);
14792                     }
14793                     
14794                     this.store.fireEvent("datachanged", this.store);
14795                     
14796                     this.onLoad();
14797                     
14798                     
14799                 }else{
14800                     
14801                     this.store.baseParams[this.queryParam] = q;
14802                     
14803                     var options = {params : this.getParams(q)};
14804                     
14805                     if(this.loadNext){
14806                         options.add = true;
14807                         options.params.start = this.page * this.pageSize;
14808                     }
14809                     
14810                     this.store.load(options);
14811                     
14812                     /*
14813                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14814                      *  we should expand the list on onLoad
14815                      *  so command out it
14816                      */
14817 //                    this.expand();
14818                 }
14819             }else{
14820                 this.selectedIndex = -1;
14821                 this.onLoad();   
14822             }
14823         }
14824         
14825         this.loadNext = false;
14826     },
14827     
14828     // private
14829     getParams : function(q){
14830         var p = {};
14831         //p[this.queryParam] = q;
14832         
14833         if(this.pageSize){
14834             p.start = 0;
14835             p.limit = this.pageSize;
14836         }
14837         return p;
14838     },
14839
14840     /**
14841      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14842      */
14843     collapse : function(){
14844         if(!this.isExpanded()){
14845             return;
14846         }
14847         
14848         this.list.hide();
14849         
14850         this.hasFocus = false;
14851         
14852         if(this.tickable){
14853             this.okBtn.hide();
14854             this.cancelBtn.hide();
14855             this.trigger.show();
14856             
14857             if(this.editable){
14858                 this.tickableInputEl().dom.value = '';
14859                 this.tickableInputEl().blur();
14860             }
14861             
14862         }
14863         
14864         Roo.get(document).un('mousedown', this.collapseIf, this);
14865         Roo.get(document).un('mousewheel', this.collapseIf, this);
14866         if (!this.editable) {
14867             Roo.get(document).un('keydown', this.listKeyPress, this);
14868         }
14869         this.fireEvent('collapse', this);
14870         
14871         this.validate();
14872     },
14873
14874     // private
14875     collapseIf : function(e){
14876         var in_combo  = e.within(this.el);
14877         var in_list =  e.within(this.list);
14878         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14879         
14880         if (in_combo || in_list || is_list) {
14881             //e.stopPropagation();
14882             return;
14883         }
14884         
14885         if(this.tickable){
14886             this.onTickableFooterButtonClick(e, false, false);
14887         }
14888
14889         this.collapse();
14890         
14891     },
14892
14893     /**
14894      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14895      */
14896     expand : function(){
14897        
14898         if(this.isExpanded() || !this.hasFocus){
14899             return;
14900         }
14901         
14902         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14903         this.list.setWidth(lw);
14904         
14905         Roo.log('expand');
14906         
14907         this.list.show();
14908         
14909         this.restrictHeight();
14910         
14911         if(this.tickable){
14912             
14913             this.tickItems = Roo.apply([], this.item);
14914             
14915             this.okBtn.show();
14916             this.cancelBtn.show();
14917             this.trigger.hide();
14918             
14919             if(this.editable){
14920                 this.tickableInputEl().focus();
14921             }
14922             
14923         }
14924         
14925         Roo.get(document).on('mousedown', this.collapseIf, this);
14926         Roo.get(document).on('mousewheel', this.collapseIf, this);
14927         if (!this.editable) {
14928             Roo.get(document).on('keydown', this.listKeyPress, this);
14929         }
14930         
14931         this.fireEvent('expand', this);
14932     },
14933
14934     // private
14935     // Implements the default empty TriggerField.onTriggerClick function
14936     onTriggerClick : function(e)
14937     {
14938         Roo.log('trigger click');
14939         
14940         if(this.disabled || !this.triggerList){
14941             return;
14942         }
14943         
14944         this.page = 0;
14945         this.loadNext = false;
14946         
14947         if(this.isExpanded()){
14948             this.collapse();
14949             if (!this.blockFocus) {
14950                 this.inputEl().focus();
14951             }
14952             
14953         }else {
14954             this.hasFocus = true;
14955             if(this.triggerAction == 'all') {
14956                 this.doQuery(this.allQuery, true);
14957             } else {
14958                 this.doQuery(this.getRawValue());
14959             }
14960             if (!this.blockFocus) {
14961                 this.inputEl().focus();
14962             }
14963         }
14964     },
14965     
14966     onTickableTriggerClick : function(e)
14967     {
14968         if(this.disabled){
14969             return;
14970         }
14971         
14972         this.page = 0;
14973         this.loadNext = false;
14974         this.hasFocus = true;
14975         
14976         if(this.triggerAction == 'all') {
14977             this.doQuery(this.allQuery, true);
14978         } else {
14979             this.doQuery(this.getRawValue());
14980         }
14981     },
14982     
14983     onSearchFieldClick : function(e)
14984     {
14985         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14986             this.onTickableFooterButtonClick(e, false, false);
14987             return;
14988         }
14989         
14990         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14991             return;
14992         }
14993         
14994         this.page = 0;
14995         this.loadNext = false;
14996         this.hasFocus = true;
14997         
14998         if(this.triggerAction == 'all') {
14999             this.doQuery(this.allQuery, true);
15000         } else {
15001             this.doQuery(this.getRawValue());
15002         }
15003     },
15004     
15005     listKeyPress : function(e)
15006     {
15007         //Roo.log('listkeypress');
15008         // scroll to first matching element based on key pres..
15009         if (e.isSpecialKey()) {
15010             return false;
15011         }
15012         var k = String.fromCharCode(e.getKey()).toUpperCase();
15013         //Roo.log(k);
15014         var match  = false;
15015         var csel = this.view.getSelectedNodes();
15016         var cselitem = false;
15017         if (csel.length) {
15018             var ix = this.view.indexOf(csel[0]);
15019             cselitem  = this.store.getAt(ix);
15020             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15021                 cselitem = false;
15022             }
15023             
15024         }
15025         
15026         this.store.each(function(v) { 
15027             if (cselitem) {
15028                 // start at existing selection.
15029                 if (cselitem.id == v.id) {
15030                     cselitem = false;
15031                 }
15032                 return true;
15033             }
15034                 
15035             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15036                 match = this.store.indexOf(v);
15037                 return false;
15038             }
15039             return true;
15040         }, this);
15041         
15042         if (match === false) {
15043             return true; // no more action?
15044         }
15045         // scroll to?
15046         this.view.select(match);
15047         var sn = Roo.get(this.view.getSelectedNodes()[0]);
15048         sn.scrollIntoView(sn.dom.parentNode, false);
15049     },
15050     
15051     onViewScroll : function(e, t){
15052         
15053         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){
15054             return;
15055         }
15056         
15057         this.hasQuery = true;
15058         
15059         this.loading = this.list.select('.loading', true).first();
15060         
15061         if(this.loading === null){
15062             this.list.createChild({
15063                 tag: 'div',
15064                 cls: 'loading roo-select2-more-results roo-select2-active',
15065                 html: 'Loading more results...'
15066             });
15067             
15068             this.loading = this.list.select('.loading', true).first();
15069             
15070             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15071             
15072             this.loading.hide();
15073         }
15074         
15075         this.loading.show();
15076         
15077         var _combo = this;
15078         
15079         this.page++;
15080         this.loadNext = true;
15081         
15082         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15083         
15084         return;
15085     },
15086     
15087     addItem : function(o)
15088     {   
15089         var dv = ''; // display value
15090         
15091         if (this.displayField) {
15092             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15093         } else {
15094             // this is an error condition!!!
15095             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15096         }
15097         
15098         if(!dv.length){
15099             return;
15100         }
15101         
15102         var choice = this.choices.createChild({
15103             tag: 'li',
15104             cls: 'roo-select2-search-choice',
15105             cn: [
15106                 {
15107                     tag: 'div',
15108                     html: dv
15109                 },
15110                 {
15111                     tag: 'a',
15112                     href: '#',
15113                     cls: 'roo-select2-search-choice-close fa fa-times',
15114                     tabindex: '-1'
15115                 }
15116             ]
15117             
15118         }, this.searchField);
15119         
15120         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15121         
15122         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15123         
15124         this.item.push(o);
15125         
15126         this.lastData = o;
15127         
15128         this.syncValue();
15129         
15130         this.inputEl().dom.value = '';
15131         
15132         this.validate();
15133     },
15134     
15135     onRemoveItem : function(e, _self, o)
15136     {
15137         e.preventDefault();
15138         
15139         this.lastItem = Roo.apply([], this.item);
15140         
15141         var index = this.item.indexOf(o.data) * 1;
15142         
15143         if( index < 0){
15144             Roo.log('not this item?!');
15145             return;
15146         }
15147         
15148         this.item.splice(index, 1);
15149         o.item.remove();
15150         
15151         this.syncValue();
15152         
15153         this.fireEvent('remove', this, e);
15154         
15155         this.validate();
15156         
15157     },
15158     
15159     syncValue : function()
15160     {
15161         if(!this.item.length){
15162             this.clearValue();
15163             return;
15164         }
15165             
15166         var value = [];
15167         var _this = this;
15168         Roo.each(this.item, function(i){
15169             if(_this.valueField){
15170                 value.push(i[_this.valueField]);
15171                 return;
15172             }
15173
15174             value.push(i);
15175         });
15176
15177         this.value = value.join(',');
15178
15179         if(this.hiddenField){
15180             this.hiddenField.dom.value = this.value;
15181         }
15182         
15183         this.store.fireEvent("datachanged", this.store);
15184         
15185         this.validate();
15186     },
15187     
15188     clearItem : function()
15189     {
15190         if(!this.multiple){
15191             return;
15192         }
15193         
15194         this.item = [];
15195         
15196         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15197            c.remove();
15198         });
15199         
15200         this.syncValue();
15201         
15202         this.validate();
15203         
15204         if(this.tickable && !Roo.isTouch){
15205             this.view.refresh();
15206         }
15207     },
15208     
15209     inputEl: function ()
15210     {
15211         if(Roo.isIOS && this.useNativeIOS){
15212             return this.el.select('select.roo-ios-select', true).first();
15213         }
15214         
15215         if(Roo.isTouch && this.mobileTouchView){
15216             return this.el.select('input.form-control',true).first();
15217         }
15218         
15219         if(this.tickable){
15220             return this.searchField;
15221         }
15222         
15223         return this.el.select('input.form-control',true).first();
15224     },
15225     
15226     onTickableFooterButtonClick : function(e, btn, el)
15227     {
15228         e.preventDefault();
15229         
15230         this.lastItem = Roo.apply([], this.item);
15231         
15232         if(btn && btn.name == 'cancel'){
15233             this.tickItems = Roo.apply([], this.item);
15234             this.collapse();
15235             return;
15236         }
15237         
15238         this.clearItem();
15239         
15240         var _this = this;
15241         
15242         Roo.each(this.tickItems, function(o){
15243             _this.addItem(o);
15244         });
15245         
15246         this.collapse();
15247         
15248     },
15249     
15250     validate : function()
15251     {
15252         if(this.getVisibilityEl().hasClass('hidden')){
15253             return true;
15254         }
15255         
15256         var v = this.getRawValue();
15257         
15258         if(this.multiple){
15259             v = this.getValue();
15260         }
15261         
15262         if(this.disabled || this.allowBlank || v.length){
15263             this.markValid();
15264             return true;
15265         }
15266         
15267         this.markInvalid();
15268         return false;
15269     },
15270     
15271     tickableInputEl : function()
15272     {
15273         if(!this.tickable || !this.editable){
15274             return this.inputEl();
15275         }
15276         
15277         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15278     },
15279     
15280     
15281     getAutoCreateTouchView : function()
15282     {
15283         var id = Roo.id();
15284         
15285         var cfg = {
15286             cls: 'form-group' //input-group
15287         };
15288         
15289         var input =  {
15290             tag: 'input',
15291             id : id,
15292             type : this.inputType,
15293             cls : 'form-control x-combo-noedit',
15294             autocomplete: 'new-password',
15295             placeholder : this.placeholder || '',
15296             readonly : true
15297         };
15298         
15299         if (this.name) {
15300             input.name = this.name;
15301         }
15302         
15303         if (this.size) {
15304             input.cls += ' input-' + this.size;
15305         }
15306         
15307         if (this.disabled) {
15308             input.disabled = true;
15309         }
15310         
15311         var inputblock = {
15312             cls : '',
15313             cn : [
15314                 input
15315             ]
15316         };
15317         
15318         if(this.before){
15319             inputblock.cls += ' input-group';
15320             
15321             inputblock.cn.unshift({
15322                 tag :'span',
15323                 cls : 'input-group-addon input-group-prepend input-group-text',
15324                 html : this.before
15325             });
15326         }
15327         
15328         if(this.removable && !this.multiple){
15329             inputblock.cls += ' roo-removable';
15330             
15331             inputblock.cn.push({
15332                 tag: 'button',
15333                 html : 'x',
15334                 cls : 'roo-combo-removable-btn close'
15335             });
15336         }
15337
15338         if(this.hasFeedback && !this.allowBlank){
15339             
15340             inputblock.cls += ' has-feedback';
15341             
15342             inputblock.cn.push({
15343                 tag: 'span',
15344                 cls: 'glyphicon form-control-feedback'
15345             });
15346             
15347         }
15348         
15349         if (this.after) {
15350             
15351             inputblock.cls += (this.before) ? '' : ' input-group';
15352             
15353             inputblock.cn.push({
15354                 tag :'span',
15355                 cls : 'input-group-addon input-group-append input-group-text',
15356                 html : this.after
15357             });
15358         }
15359
15360         
15361         var ibwrap = inputblock;
15362         
15363         if(this.multiple){
15364             ibwrap = {
15365                 tag: 'ul',
15366                 cls: 'roo-select2-choices',
15367                 cn:[
15368                     {
15369                         tag: 'li',
15370                         cls: 'roo-select2-search-field',
15371                         cn: [
15372
15373                             inputblock
15374                         ]
15375                     }
15376                 ]
15377             };
15378         
15379             
15380         }
15381         
15382         var combobox = {
15383             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15384             cn: [
15385                 {
15386                     tag: 'input',
15387                     type : 'hidden',
15388                     cls: 'form-hidden-field'
15389                 },
15390                 ibwrap
15391             ]
15392         };
15393         
15394         if(!this.multiple && this.showToggleBtn){
15395             
15396             var caret = {
15397                 cls: 'caret'
15398             };
15399             
15400             if (this.caret != false) {
15401                 caret = {
15402                      tag: 'i',
15403                      cls: 'fa fa-' + this.caret
15404                 };
15405                 
15406             }
15407             
15408             combobox.cn.push({
15409                 tag :'span',
15410                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15411                 cn : [
15412                     Roo.bootstrap.version == 3 ? caret : '',
15413                     {
15414                         tag: 'span',
15415                         cls: 'combobox-clear',
15416                         cn  : [
15417                             {
15418                                 tag : 'i',
15419                                 cls: 'icon-remove'
15420                             }
15421                         ]
15422                     }
15423                 ]
15424
15425             })
15426         }
15427         
15428         if(this.multiple){
15429             combobox.cls += ' roo-select2-container-multi';
15430         }
15431         
15432         var align = this.labelAlign || this.parentLabelAlign();
15433         
15434         if (align ==='left' && this.fieldLabel.length) {
15435
15436             cfg.cn = [
15437                 {
15438                    tag : 'i',
15439                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15440                    tooltip : 'This field is required'
15441                 },
15442                 {
15443                     tag: 'label',
15444                     cls : 'control-label col-form-label',
15445                     html : this.fieldLabel
15446
15447                 },
15448                 {
15449                     cls : '', 
15450                     cn: [
15451                         combobox
15452                     ]
15453                 }
15454             ];
15455             
15456             var labelCfg = cfg.cn[1];
15457             var contentCfg = cfg.cn[2];
15458             
15459
15460             if(this.indicatorpos == 'right'){
15461                 cfg.cn = [
15462                     {
15463                         tag: 'label',
15464                         'for' :  id,
15465                         cls : 'control-label col-form-label',
15466                         cn : [
15467                             {
15468                                 tag : 'span',
15469                                 html : this.fieldLabel
15470                             },
15471                             {
15472                                 tag : 'i',
15473                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15474                                 tooltip : 'This field is required'
15475                             }
15476                         ]
15477                     },
15478                     {
15479                         cls : "",
15480                         cn: [
15481                             combobox
15482                         ]
15483                     }
15484
15485                 ];
15486                 
15487                 labelCfg = cfg.cn[0];
15488                 contentCfg = cfg.cn[1];
15489             }
15490             
15491            
15492             
15493             if(this.labelWidth > 12){
15494                 labelCfg.style = "width: " + this.labelWidth + 'px';
15495             }
15496             
15497             if(this.labelWidth < 13 && this.labelmd == 0){
15498                 this.labelmd = this.labelWidth;
15499             }
15500             
15501             if(this.labellg > 0){
15502                 labelCfg.cls += ' col-lg-' + this.labellg;
15503                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15504             }
15505             
15506             if(this.labelmd > 0){
15507                 labelCfg.cls += ' col-md-' + this.labelmd;
15508                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15509             }
15510             
15511             if(this.labelsm > 0){
15512                 labelCfg.cls += ' col-sm-' + this.labelsm;
15513                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15514             }
15515             
15516             if(this.labelxs > 0){
15517                 labelCfg.cls += ' col-xs-' + this.labelxs;
15518                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15519             }
15520                 
15521                 
15522         } else if ( this.fieldLabel.length) {
15523             cfg.cn = [
15524                 {
15525                    tag : 'i',
15526                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15527                    tooltip : 'This field is required'
15528                 },
15529                 {
15530                     tag: 'label',
15531                     cls : 'control-label',
15532                     html : this.fieldLabel
15533
15534                 },
15535                 {
15536                     cls : '', 
15537                     cn: [
15538                         combobox
15539                     ]
15540                 }
15541             ];
15542             
15543             if(this.indicatorpos == 'right'){
15544                 cfg.cn = [
15545                     {
15546                         tag: 'label',
15547                         cls : 'control-label',
15548                         html : this.fieldLabel,
15549                         cn : [
15550                             {
15551                                tag : 'i',
15552                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15553                                tooltip : 'This field is required'
15554                             }
15555                         ]
15556                     },
15557                     {
15558                         cls : '', 
15559                         cn: [
15560                             combobox
15561                         ]
15562                     }
15563                 ];
15564             }
15565         } else {
15566             cfg.cn = combobox;    
15567         }
15568         
15569         
15570         var settings = this;
15571         
15572         ['xs','sm','md','lg'].map(function(size){
15573             if (settings[size]) {
15574                 cfg.cls += ' col-' + size + '-' + settings[size];
15575             }
15576         });
15577         
15578         return cfg;
15579     },
15580     
15581     initTouchView : function()
15582     {
15583         this.renderTouchView();
15584         
15585         this.touchViewEl.on('scroll', function(){
15586             this.el.dom.scrollTop = 0;
15587         }, this);
15588         
15589         this.originalValue = this.getValue();
15590         
15591         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15592         
15593         this.inputEl().on("click", this.showTouchView, this);
15594         if (this.triggerEl) {
15595             this.triggerEl.on("click", this.showTouchView, this);
15596         }
15597         
15598         
15599         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15600         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15601         
15602         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15603         
15604         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15605         this.store.on('load', this.onTouchViewLoad, this);
15606         this.store.on('loadexception', this.onTouchViewLoadException, this);
15607         
15608         if(this.hiddenName){
15609             
15610             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15611             
15612             this.hiddenField.dom.value =
15613                 this.hiddenValue !== undefined ? this.hiddenValue :
15614                 this.value !== undefined ? this.value : '';
15615         
15616             this.el.dom.removeAttribute('name');
15617             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15618         }
15619         
15620         if(this.multiple){
15621             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15622             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15623         }
15624         
15625         if(this.removable && !this.multiple){
15626             var close = this.closeTriggerEl();
15627             if(close){
15628                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15629                 close.on('click', this.removeBtnClick, this, close);
15630             }
15631         }
15632         /*
15633          * fix the bug in Safari iOS8
15634          */
15635         this.inputEl().on("focus", function(e){
15636             document.activeElement.blur();
15637         }, this);
15638         
15639         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15640         
15641         return;
15642         
15643         
15644     },
15645     
15646     renderTouchView : function()
15647     {
15648         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15649         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15650         
15651         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15652         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15653         
15654         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15655         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15656         this.touchViewBodyEl.setStyle('overflow', 'auto');
15657         
15658         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15659         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15660         
15661         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15662         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15663         
15664     },
15665     
15666     showTouchView : function()
15667     {
15668         if(this.disabled){
15669             return;
15670         }
15671         
15672         this.touchViewHeaderEl.hide();
15673
15674         if(this.modalTitle.length){
15675             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15676             this.touchViewHeaderEl.show();
15677         }
15678
15679         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15680         this.touchViewEl.show();
15681
15682         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15683         
15684         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15685         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15686
15687         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15688
15689         if(this.modalTitle.length){
15690             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15691         }
15692         
15693         this.touchViewBodyEl.setHeight(bodyHeight);
15694
15695         if(this.animate){
15696             var _this = this;
15697             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15698         }else{
15699             this.touchViewEl.addClass('in');
15700         }
15701         
15702         if(this._touchViewMask){
15703             Roo.get(document.body).addClass("x-body-masked");
15704             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15705             this._touchViewMask.setStyle('z-index', 10000);
15706             this._touchViewMask.addClass('show');
15707         }
15708         
15709         this.doTouchViewQuery();
15710         
15711     },
15712     
15713     hideTouchView : function()
15714     {
15715         this.touchViewEl.removeClass('in');
15716
15717         if(this.animate){
15718             var _this = this;
15719             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15720         }else{
15721             this.touchViewEl.setStyle('display', 'none');
15722         }
15723         
15724         if(this._touchViewMask){
15725             this._touchViewMask.removeClass('show');
15726             Roo.get(document.body).removeClass("x-body-masked");
15727         }
15728     },
15729     
15730     setTouchViewValue : function()
15731     {
15732         if(this.multiple){
15733             this.clearItem();
15734         
15735             var _this = this;
15736
15737             Roo.each(this.tickItems, function(o){
15738                 this.addItem(o);
15739             }, this);
15740         }
15741         
15742         this.hideTouchView();
15743     },
15744     
15745     doTouchViewQuery : function()
15746     {
15747         var qe = {
15748             query: '',
15749             forceAll: true,
15750             combo: this,
15751             cancel:false
15752         };
15753         
15754         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15755             return false;
15756         }
15757         
15758         if(!this.alwaysQuery || this.mode == 'local'){
15759             this.onTouchViewLoad();
15760             return;
15761         }
15762         
15763         this.store.load();
15764     },
15765     
15766     onTouchViewBeforeLoad : function(combo,opts)
15767     {
15768         return;
15769     },
15770
15771     // private
15772     onTouchViewLoad : function()
15773     {
15774         if(this.store.getCount() < 1){
15775             this.onTouchViewEmptyResults();
15776             return;
15777         }
15778         
15779         this.clearTouchView();
15780         
15781         var rawValue = this.getRawValue();
15782         
15783         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15784         
15785         this.tickItems = [];
15786         
15787         this.store.data.each(function(d, rowIndex){
15788             var row = this.touchViewListGroup.createChild(template);
15789             
15790             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15791                 row.addClass(d.data.cls);
15792             }
15793             
15794             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15795                 var cfg = {
15796                     data : d.data,
15797                     html : d.data[this.displayField]
15798                 };
15799                 
15800                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15801                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15802                 }
15803             }
15804             row.removeClass('selected');
15805             if(!this.multiple && this.valueField &&
15806                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15807             {
15808                 // radio buttons..
15809                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15810                 row.addClass('selected');
15811             }
15812             
15813             if(this.multiple && this.valueField &&
15814                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15815             {
15816                 
15817                 // checkboxes...
15818                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15819                 this.tickItems.push(d.data);
15820             }
15821             
15822             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15823             
15824         }, this);
15825         
15826         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15827         
15828         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15829
15830         if(this.modalTitle.length){
15831             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15832         }
15833
15834         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15835         
15836         if(this.mobile_restrict_height && listHeight < bodyHeight){
15837             this.touchViewBodyEl.setHeight(listHeight);
15838         }
15839         
15840         var _this = this;
15841         
15842         if(firstChecked && listHeight > bodyHeight){
15843             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15844         }
15845         
15846     },
15847     
15848     onTouchViewLoadException : function()
15849     {
15850         this.hideTouchView();
15851     },
15852     
15853     onTouchViewEmptyResults : function()
15854     {
15855         this.clearTouchView();
15856         
15857         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15858         
15859         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15860         
15861     },
15862     
15863     clearTouchView : function()
15864     {
15865         this.touchViewListGroup.dom.innerHTML = '';
15866     },
15867     
15868     onTouchViewClick : function(e, el, o)
15869     {
15870         e.preventDefault();
15871         
15872         var row = o.row;
15873         var rowIndex = o.rowIndex;
15874         
15875         var r = this.store.getAt(rowIndex);
15876         
15877         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15878             
15879             if(!this.multiple){
15880                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15881                     c.dom.removeAttribute('checked');
15882                 }, this);
15883
15884                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15885
15886                 this.setFromData(r.data);
15887
15888                 var close = this.closeTriggerEl();
15889
15890                 if(close){
15891                     close.show();
15892                 }
15893
15894                 this.hideTouchView();
15895
15896                 this.fireEvent('select', this, r, rowIndex);
15897
15898                 return;
15899             }
15900
15901             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15902                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15903                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15904                 return;
15905             }
15906
15907             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15908             this.addItem(r.data);
15909             this.tickItems.push(r.data);
15910         }
15911     },
15912     
15913     getAutoCreateNativeIOS : function()
15914     {
15915         var cfg = {
15916             cls: 'form-group' //input-group,
15917         };
15918         
15919         var combobox =  {
15920             tag: 'select',
15921             cls : 'roo-ios-select'
15922         };
15923         
15924         if (this.name) {
15925             combobox.name = this.name;
15926         }
15927         
15928         if (this.disabled) {
15929             combobox.disabled = true;
15930         }
15931         
15932         var settings = this;
15933         
15934         ['xs','sm','md','lg'].map(function(size){
15935             if (settings[size]) {
15936                 cfg.cls += ' col-' + size + '-' + settings[size];
15937             }
15938         });
15939         
15940         cfg.cn = combobox;
15941         
15942         return cfg;
15943         
15944     },
15945     
15946     initIOSView : function()
15947     {
15948         this.store.on('load', this.onIOSViewLoad, this);
15949         
15950         return;
15951     },
15952     
15953     onIOSViewLoad : function()
15954     {
15955         if(this.store.getCount() < 1){
15956             return;
15957         }
15958         
15959         this.clearIOSView();
15960         
15961         if(this.allowBlank) {
15962             
15963             var default_text = '-- SELECT --';
15964             
15965             if(this.placeholder.length){
15966                 default_text = this.placeholder;
15967             }
15968             
15969             if(this.emptyTitle.length){
15970                 default_text += ' - ' + this.emptyTitle + ' -';
15971             }
15972             
15973             var opt = this.inputEl().createChild({
15974                 tag: 'option',
15975                 value : 0,
15976                 html : default_text
15977             });
15978             
15979             var o = {};
15980             o[this.valueField] = 0;
15981             o[this.displayField] = default_text;
15982             
15983             this.ios_options.push({
15984                 data : o,
15985                 el : opt
15986             });
15987             
15988         }
15989         
15990         this.store.data.each(function(d, rowIndex){
15991             
15992             var html = '';
15993             
15994             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15995                 html = d.data[this.displayField];
15996             }
15997             
15998             var value = '';
15999             
16000             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
16001                 value = d.data[this.valueField];
16002             }
16003             
16004             var option = {
16005                 tag: 'option',
16006                 value : value,
16007                 html : html
16008             };
16009             
16010             if(this.value == d.data[this.valueField]){
16011                 option['selected'] = true;
16012             }
16013             
16014             var opt = this.inputEl().createChild(option);
16015             
16016             this.ios_options.push({
16017                 data : d.data,
16018                 el : opt
16019             });
16020             
16021         }, this);
16022         
16023         this.inputEl().on('change', function(){
16024            this.fireEvent('select', this);
16025         }, this);
16026         
16027     },
16028     
16029     clearIOSView: function()
16030     {
16031         this.inputEl().dom.innerHTML = '';
16032         
16033         this.ios_options = [];
16034     },
16035     
16036     setIOSValue: function(v)
16037     {
16038         this.value = v;
16039         
16040         if(!this.ios_options){
16041             return;
16042         }
16043         
16044         Roo.each(this.ios_options, function(opts){
16045            
16046            opts.el.dom.removeAttribute('selected');
16047            
16048            if(opts.data[this.valueField] != v){
16049                return;
16050            }
16051            
16052            opts.el.dom.setAttribute('selected', true);
16053            
16054         }, this);
16055     }
16056
16057     /** 
16058     * @cfg {Boolean} grow 
16059     * @hide 
16060     */
16061     /** 
16062     * @cfg {Number} growMin 
16063     * @hide 
16064     */
16065     /** 
16066     * @cfg {Number} growMax 
16067     * @hide 
16068     */
16069     /**
16070      * @hide
16071      * @method autoSize
16072      */
16073 });
16074
16075 Roo.apply(Roo.bootstrap.ComboBox,  {
16076     
16077     header : {
16078         tag: 'div',
16079         cls: 'modal-header',
16080         cn: [
16081             {
16082                 tag: 'h4',
16083                 cls: 'modal-title'
16084             }
16085         ]
16086     },
16087     
16088     body : {
16089         tag: 'div',
16090         cls: 'modal-body',
16091         cn: [
16092             {
16093                 tag: 'ul',
16094                 cls: 'list-group'
16095             }
16096         ]
16097     },
16098     
16099     listItemRadio : {
16100         tag: 'li',
16101         cls: 'list-group-item',
16102         cn: [
16103             {
16104                 tag: 'span',
16105                 cls: 'roo-combobox-list-group-item-value'
16106             },
16107             {
16108                 tag: 'div',
16109                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16110                 cn: [
16111                     {
16112                         tag: 'input',
16113                         type: 'radio'
16114                     },
16115                     {
16116                         tag: 'label'
16117                     }
16118                 ]
16119             }
16120         ]
16121     },
16122     
16123     listItemCheckbox : {
16124         tag: 'li',
16125         cls: 'list-group-item',
16126         cn: [
16127             {
16128                 tag: 'span',
16129                 cls: 'roo-combobox-list-group-item-value'
16130             },
16131             {
16132                 tag: 'div',
16133                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16134                 cn: [
16135                     {
16136                         tag: 'input',
16137                         type: 'checkbox'
16138                     },
16139                     {
16140                         tag: 'label'
16141                     }
16142                 ]
16143             }
16144         ]
16145     },
16146     
16147     emptyResult : {
16148         tag: 'div',
16149         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16150     },
16151     
16152     footer : {
16153         tag: 'div',
16154         cls: 'modal-footer',
16155         cn: [
16156             {
16157                 tag: 'div',
16158                 cls: 'row',
16159                 cn: [
16160                     {
16161                         tag: 'div',
16162                         cls: 'col-xs-6 text-left',
16163                         cn: {
16164                             tag: 'button',
16165                             cls: 'btn btn-danger roo-touch-view-cancel',
16166                             html: 'Cancel'
16167                         }
16168                     },
16169                     {
16170                         tag: 'div',
16171                         cls: 'col-xs-6 text-right',
16172                         cn: {
16173                             tag: 'button',
16174                             cls: 'btn btn-success roo-touch-view-ok',
16175                             html: 'OK'
16176                         }
16177                     }
16178                 ]
16179             }
16180         ]
16181         
16182     }
16183 });
16184
16185 Roo.apply(Roo.bootstrap.ComboBox,  {
16186     
16187     touchViewTemplate : {
16188         tag: 'div',
16189         cls: 'modal fade roo-combobox-touch-view',
16190         cn: [
16191             {
16192                 tag: 'div',
16193                 cls: 'modal-dialog',
16194                 style : 'position:fixed', // we have to fix position....
16195                 cn: [
16196                     {
16197                         tag: 'div',
16198                         cls: 'modal-content',
16199                         cn: [
16200                             Roo.bootstrap.ComboBox.header,
16201                             Roo.bootstrap.ComboBox.body,
16202                             Roo.bootstrap.ComboBox.footer
16203                         ]
16204                     }
16205                 ]
16206             }
16207         ]
16208     }
16209 });/*
16210  * Based on:
16211  * Ext JS Library 1.1.1
16212  * Copyright(c) 2006-2007, Ext JS, LLC.
16213  *
16214  * Originally Released Under LGPL - original licence link has changed is not relivant.
16215  *
16216  * Fork - LGPL
16217  * <script type="text/javascript">
16218  */
16219
16220 /**
16221  * @class Roo.View
16222  * @extends Roo.util.Observable
16223  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16224  * This class also supports single and multi selection modes. <br>
16225  * Create a data model bound view:
16226  <pre><code>
16227  var store = new Roo.data.Store(...);
16228
16229  var view = new Roo.View({
16230     el : "my-element",
16231     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16232  
16233     singleSelect: true,
16234     selectedClass: "ydataview-selected",
16235     store: store
16236  });
16237
16238  // listen for node click?
16239  view.on("click", function(vw, index, node, e){
16240  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16241  });
16242
16243  // load XML data
16244  dataModel.load("foobar.xml");
16245  </code></pre>
16246  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16247  * <br><br>
16248  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16249  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16250  * 
16251  * Note: old style constructor is still suported (container, template, config)
16252  * 
16253  * @constructor
16254  * Create a new View
16255  * @param {Object} config The config object
16256  * 
16257  */
16258 Roo.View = function(config, depreciated_tpl, depreciated_config){
16259     
16260     this.parent = false;
16261     
16262     if (typeof(depreciated_tpl) == 'undefined') {
16263         // new way.. - universal constructor.
16264         Roo.apply(this, config);
16265         this.el  = Roo.get(this.el);
16266     } else {
16267         // old format..
16268         this.el  = Roo.get(config);
16269         this.tpl = depreciated_tpl;
16270         Roo.apply(this, depreciated_config);
16271     }
16272     this.wrapEl  = this.el.wrap().wrap();
16273     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16274     
16275     
16276     if(typeof(this.tpl) == "string"){
16277         this.tpl = new Roo.Template(this.tpl);
16278     } else {
16279         // support xtype ctors..
16280         this.tpl = new Roo.factory(this.tpl, Roo);
16281     }
16282     
16283     
16284     this.tpl.compile();
16285     
16286     /** @private */
16287     this.addEvents({
16288         /**
16289          * @event beforeclick
16290          * Fires before a click is processed. Returns false to cancel the default action.
16291          * @param {Roo.View} this
16292          * @param {Number} index The index of the target node
16293          * @param {HTMLElement} node The target node
16294          * @param {Roo.EventObject} e The raw event object
16295          */
16296             "beforeclick" : true,
16297         /**
16298          * @event click
16299          * Fires when a template node is clicked.
16300          * @param {Roo.View} this
16301          * @param {Number} index The index of the target node
16302          * @param {HTMLElement} node The target node
16303          * @param {Roo.EventObject} e The raw event object
16304          */
16305             "click" : true,
16306         /**
16307          * @event dblclick
16308          * Fires when a template node is double clicked.
16309          * @param {Roo.View} this
16310          * @param {Number} index The index of the target node
16311          * @param {HTMLElement} node The target node
16312          * @param {Roo.EventObject} e The raw event object
16313          */
16314             "dblclick" : true,
16315         /**
16316          * @event contextmenu
16317          * Fires when a template node is right clicked.
16318          * @param {Roo.View} this
16319          * @param {Number} index The index of the target node
16320          * @param {HTMLElement} node The target node
16321          * @param {Roo.EventObject} e The raw event object
16322          */
16323             "contextmenu" : true,
16324         /**
16325          * @event selectionchange
16326          * Fires when the selected nodes change.
16327          * @param {Roo.View} this
16328          * @param {Array} selections Array of the selected nodes
16329          */
16330             "selectionchange" : true,
16331     
16332         /**
16333          * @event beforeselect
16334          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16335          * @param {Roo.View} this
16336          * @param {HTMLElement} node The node to be selected
16337          * @param {Array} selections Array of currently selected nodes
16338          */
16339             "beforeselect" : true,
16340         /**
16341          * @event preparedata
16342          * Fires on every row to render, to allow you to change the data.
16343          * @param {Roo.View} this
16344          * @param {Object} data to be rendered (change this)
16345          */
16346           "preparedata" : true
16347           
16348           
16349         });
16350
16351
16352
16353     this.el.on({
16354         "click": this.onClick,
16355         "dblclick": this.onDblClick,
16356         "contextmenu": this.onContextMenu,
16357         scope:this
16358     });
16359
16360     this.selections = [];
16361     this.nodes = [];
16362     this.cmp = new Roo.CompositeElementLite([]);
16363     if(this.store){
16364         this.store = Roo.factory(this.store, Roo.data);
16365         this.setStore(this.store, true);
16366     }
16367     
16368     if ( this.footer && this.footer.xtype) {
16369            
16370          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16371         
16372         this.footer.dataSource = this.store;
16373         this.footer.container = fctr;
16374         this.footer = Roo.factory(this.footer, Roo);
16375         fctr.insertFirst(this.el);
16376         
16377         // this is a bit insane - as the paging toolbar seems to detach the el..
16378 //        dom.parentNode.parentNode.parentNode
16379          // they get detached?
16380     }
16381     
16382     
16383     Roo.View.superclass.constructor.call(this);
16384     
16385     
16386 };
16387
16388 Roo.extend(Roo.View, Roo.util.Observable, {
16389     
16390      /**
16391      * @cfg {Roo.data.Store} store Data store to load data from.
16392      */
16393     store : false,
16394     
16395     /**
16396      * @cfg {String|Roo.Element} el The container element.
16397      */
16398     el : '',
16399     
16400     /**
16401      * @cfg {String|Roo.Template} tpl The template used by this View 
16402      */
16403     tpl : false,
16404     /**
16405      * @cfg {String} dataName the named area of the template to use as the data area
16406      *                          Works with domtemplates roo-name="name"
16407      */
16408     dataName: false,
16409     /**
16410      * @cfg {String} selectedClass The css class to add to selected nodes
16411      */
16412     selectedClass : "x-view-selected",
16413      /**
16414      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16415      */
16416     emptyText : "",
16417     
16418     /**
16419      * @cfg {String} text to display on mask (default Loading)
16420      */
16421     mask : false,
16422     /**
16423      * @cfg {Boolean} multiSelect Allow multiple selection
16424      */
16425     multiSelect : false,
16426     /**
16427      * @cfg {Boolean} singleSelect Allow single selection
16428      */
16429     singleSelect:  false,
16430     
16431     /**
16432      * @cfg {Boolean} toggleSelect - selecting 
16433      */
16434     toggleSelect : false,
16435     
16436     /**
16437      * @cfg {Boolean} tickable - selecting 
16438      */
16439     tickable : false,
16440     
16441     /**
16442      * Returns the element this view is bound to.
16443      * @return {Roo.Element}
16444      */
16445     getEl : function(){
16446         return this.wrapEl;
16447     },
16448     
16449     
16450
16451     /**
16452      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16453      */
16454     refresh : function(){
16455         //Roo.log('refresh');
16456         var t = this.tpl;
16457         
16458         // if we are using something like 'domtemplate', then
16459         // the what gets used is:
16460         // t.applySubtemplate(NAME, data, wrapping data..)
16461         // the outer template then get' applied with
16462         //     the store 'extra data'
16463         // and the body get's added to the
16464         //      roo-name="data" node?
16465         //      <span class='roo-tpl-{name}'></span> ?????
16466         
16467         
16468         
16469         this.clearSelections();
16470         this.el.update("");
16471         var html = [];
16472         var records = this.store.getRange();
16473         if(records.length < 1) {
16474             
16475             // is this valid??  = should it render a template??
16476             
16477             this.el.update(this.emptyText);
16478             return;
16479         }
16480         var el = this.el;
16481         if (this.dataName) {
16482             this.el.update(t.apply(this.store.meta)); //????
16483             el = this.el.child('.roo-tpl-' + this.dataName);
16484         }
16485         
16486         for(var i = 0, len = records.length; i < len; i++){
16487             var data = this.prepareData(records[i].data, i, records[i]);
16488             this.fireEvent("preparedata", this, data, i, records[i]);
16489             
16490             var d = Roo.apply({}, data);
16491             
16492             if(this.tickable){
16493                 Roo.apply(d, {'roo-id' : Roo.id()});
16494                 
16495                 var _this = this;
16496             
16497                 Roo.each(this.parent.item, function(item){
16498                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16499                         return;
16500                     }
16501                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16502                 });
16503             }
16504             
16505             html[html.length] = Roo.util.Format.trim(
16506                 this.dataName ?
16507                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16508                     t.apply(d)
16509             );
16510         }
16511         
16512         
16513         
16514         el.update(html.join(""));
16515         this.nodes = el.dom.childNodes;
16516         this.updateIndexes(0);
16517     },
16518     
16519
16520     /**
16521      * Function to override to reformat the data that is sent to
16522      * the template for each node.
16523      * DEPRICATED - use the preparedata event handler.
16524      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16525      * a JSON object for an UpdateManager bound view).
16526      */
16527     prepareData : function(data, index, record)
16528     {
16529         this.fireEvent("preparedata", this, data, index, record);
16530         return data;
16531     },
16532
16533     onUpdate : function(ds, record){
16534         // Roo.log('on update');   
16535         this.clearSelections();
16536         var index = this.store.indexOf(record);
16537         var n = this.nodes[index];
16538         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16539         n.parentNode.removeChild(n);
16540         this.updateIndexes(index, index);
16541     },
16542
16543     
16544     
16545 // --------- FIXME     
16546     onAdd : function(ds, records, index)
16547     {
16548         //Roo.log(['on Add', ds, records, index] );        
16549         this.clearSelections();
16550         if(this.nodes.length == 0){
16551             this.refresh();
16552             return;
16553         }
16554         var n = this.nodes[index];
16555         for(var i = 0, len = records.length; i < len; i++){
16556             var d = this.prepareData(records[i].data, i, records[i]);
16557             if(n){
16558                 this.tpl.insertBefore(n, d);
16559             }else{
16560                 
16561                 this.tpl.append(this.el, d);
16562             }
16563         }
16564         this.updateIndexes(index);
16565     },
16566
16567     onRemove : function(ds, record, index){
16568        // Roo.log('onRemove');
16569         this.clearSelections();
16570         var el = this.dataName  ?
16571             this.el.child('.roo-tpl-' + this.dataName) :
16572             this.el; 
16573         
16574         el.dom.removeChild(this.nodes[index]);
16575         this.updateIndexes(index);
16576     },
16577
16578     /**
16579      * Refresh an individual node.
16580      * @param {Number} index
16581      */
16582     refreshNode : function(index){
16583         this.onUpdate(this.store, this.store.getAt(index));
16584     },
16585
16586     updateIndexes : function(startIndex, endIndex){
16587         var ns = this.nodes;
16588         startIndex = startIndex || 0;
16589         endIndex = endIndex || ns.length - 1;
16590         for(var i = startIndex; i <= endIndex; i++){
16591             ns[i].nodeIndex = i;
16592         }
16593     },
16594
16595     /**
16596      * Changes the data store this view uses and refresh the view.
16597      * @param {Store} store
16598      */
16599     setStore : function(store, initial){
16600         if(!initial && this.store){
16601             this.store.un("datachanged", this.refresh);
16602             this.store.un("add", this.onAdd);
16603             this.store.un("remove", this.onRemove);
16604             this.store.un("update", this.onUpdate);
16605             this.store.un("clear", this.refresh);
16606             this.store.un("beforeload", this.onBeforeLoad);
16607             this.store.un("load", this.onLoad);
16608             this.store.un("loadexception", this.onLoad);
16609         }
16610         if(store){
16611           
16612             store.on("datachanged", this.refresh, this);
16613             store.on("add", this.onAdd, this);
16614             store.on("remove", this.onRemove, this);
16615             store.on("update", this.onUpdate, this);
16616             store.on("clear", this.refresh, this);
16617             store.on("beforeload", this.onBeforeLoad, this);
16618             store.on("load", this.onLoad, this);
16619             store.on("loadexception", this.onLoad, this);
16620         }
16621         
16622         if(store){
16623             this.refresh();
16624         }
16625     },
16626     /**
16627      * onbeforeLoad - masks the loading area.
16628      *
16629      */
16630     onBeforeLoad : function(store,opts)
16631     {
16632          //Roo.log('onBeforeLoad');   
16633         if (!opts.add) {
16634             this.el.update("");
16635         }
16636         this.el.mask(this.mask ? this.mask : "Loading" ); 
16637     },
16638     onLoad : function ()
16639     {
16640         this.el.unmask();
16641     },
16642     
16643
16644     /**
16645      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16646      * @param {HTMLElement} node
16647      * @return {HTMLElement} The template node
16648      */
16649     findItemFromChild : function(node){
16650         var el = this.dataName  ?
16651             this.el.child('.roo-tpl-' + this.dataName,true) :
16652             this.el.dom; 
16653         
16654         if(!node || node.parentNode == el){
16655                     return node;
16656             }
16657             var p = node.parentNode;
16658             while(p && p != el){
16659             if(p.parentNode == el){
16660                 return p;
16661             }
16662             p = p.parentNode;
16663         }
16664             return null;
16665     },
16666
16667     /** @ignore */
16668     onClick : function(e){
16669         var item = this.findItemFromChild(e.getTarget());
16670         if(item){
16671             var index = this.indexOf(item);
16672             if(this.onItemClick(item, index, e) !== false){
16673                 this.fireEvent("click", this, index, item, e);
16674             }
16675         }else{
16676             this.clearSelections();
16677         }
16678     },
16679
16680     /** @ignore */
16681     onContextMenu : function(e){
16682         var item = this.findItemFromChild(e.getTarget());
16683         if(item){
16684             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16685         }
16686     },
16687
16688     /** @ignore */
16689     onDblClick : function(e){
16690         var item = this.findItemFromChild(e.getTarget());
16691         if(item){
16692             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16693         }
16694     },
16695
16696     onItemClick : function(item, index, e)
16697     {
16698         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16699             return false;
16700         }
16701         if (this.toggleSelect) {
16702             var m = this.isSelected(item) ? 'unselect' : 'select';
16703             //Roo.log(m);
16704             var _t = this;
16705             _t[m](item, true, false);
16706             return true;
16707         }
16708         if(this.multiSelect || this.singleSelect){
16709             if(this.multiSelect && e.shiftKey && this.lastSelection){
16710                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16711             }else{
16712                 this.select(item, this.multiSelect && e.ctrlKey);
16713                 this.lastSelection = item;
16714             }
16715             
16716             if(!this.tickable){
16717                 e.preventDefault();
16718             }
16719             
16720         }
16721         return true;
16722     },
16723
16724     /**
16725      * Get the number of selected nodes.
16726      * @return {Number}
16727      */
16728     getSelectionCount : function(){
16729         return this.selections.length;
16730     },
16731
16732     /**
16733      * Get the currently selected nodes.
16734      * @return {Array} An array of HTMLElements
16735      */
16736     getSelectedNodes : function(){
16737         return this.selections;
16738     },
16739
16740     /**
16741      * Get the indexes of the selected nodes.
16742      * @return {Array}
16743      */
16744     getSelectedIndexes : function(){
16745         var indexes = [], s = this.selections;
16746         for(var i = 0, len = s.length; i < len; i++){
16747             indexes.push(s[i].nodeIndex);
16748         }
16749         return indexes;
16750     },
16751
16752     /**
16753      * Clear all selections
16754      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16755      */
16756     clearSelections : function(suppressEvent){
16757         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16758             this.cmp.elements = this.selections;
16759             this.cmp.removeClass(this.selectedClass);
16760             this.selections = [];
16761             if(!suppressEvent){
16762                 this.fireEvent("selectionchange", this, this.selections);
16763             }
16764         }
16765     },
16766
16767     /**
16768      * Returns true if the passed node is selected
16769      * @param {HTMLElement/Number} node The node or node index
16770      * @return {Boolean}
16771      */
16772     isSelected : function(node){
16773         var s = this.selections;
16774         if(s.length < 1){
16775             return false;
16776         }
16777         node = this.getNode(node);
16778         return s.indexOf(node) !== -1;
16779     },
16780
16781     /**
16782      * Selects 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 to keep existing selections
16785      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16786      */
16787     select : function(nodeInfo, keepExisting, suppressEvent){
16788         if(nodeInfo instanceof Array){
16789             if(!keepExisting){
16790                 this.clearSelections(true);
16791             }
16792             for(var i = 0, len = nodeInfo.length; i < len; i++){
16793                 this.select(nodeInfo[i], true, true);
16794             }
16795             return;
16796         } 
16797         var node = this.getNode(nodeInfo);
16798         if(!node || this.isSelected(node)){
16799             return; // already selected.
16800         }
16801         if(!keepExisting){
16802             this.clearSelections(true);
16803         }
16804         
16805         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16806             Roo.fly(node).addClass(this.selectedClass);
16807             this.selections.push(node);
16808             if(!suppressEvent){
16809                 this.fireEvent("selectionchange", this, this.selections);
16810             }
16811         }
16812         
16813         
16814     },
16815       /**
16816      * Unselects nodes.
16817      * @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
16818      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16819      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16820      */
16821     unselect : function(nodeInfo, keepExisting, suppressEvent)
16822     {
16823         if(nodeInfo instanceof Array){
16824             Roo.each(this.selections, function(s) {
16825                 this.unselect(s, nodeInfo);
16826             }, this);
16827             return;
16828         }
16829         var node = this.getNode(nodeInfo);
16830         if(!node || !this.isSelected(node)){
16831             //Roo.log("not selected");
16832             return; // not selected.
16833         }
16834         // fireevent???
16835         var ns = [];
16836         Roo.each(this.selections, function(s) {
16837             if (s == node ) {
16838                 Roo.fly(node).removeClass(this.selectedClass);
16839
16840                 return;
16841             }
16842             ns.push(s);
16843         },this);
16844         
16845         this.selections= ns;
16846         this.fireEvent("selectionchange", this, this.selections);
16847     },
16848
16849     /**
16850      * Gets a template node.
16851      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16852      * @return {HTMLElement} The node or null if it wasn't found
16853      */
16854     getNode : function(nodeInfo){
16855         if(typeof nodeInfo == "string"){
16856             return document.getElementById(nodeInfo);
16857         }else if(typeof nodeInfo == "number"){
16858             return this.nodes[nodeInfo];
16859         }
16860         return nodeInfo;
16861     },
16862
16863     /**
16864      * Gets a range template nodes.
16865      * @param {Number} startIndex
16866      * @param {Number} endIndex
16867      * @return {Array} An array of nodes
16868      */
16869     getNodes : function(start, end){
16870         var ns = this.nodes;
16871         start = start || 0;
16872         end = typeof end == "undefined" ? ns.length - 1 : end;
16873         var nodes = [];
16874         if(start <= end){
16875             for(var i = start; i <= end; i++){
16876                 nodes.push(ns[i]);
16877             }
16878         } else{
16879             for(var i = start; i >= end; i--){
16880                 nodes.push(ns[i]);
16881             }
16882         }
16883         return nodes;
16884     },
16885
16886     /**
16887      * Finds the index of the passed node
16888      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16889      * @return {Number} The index of the node or -1
16890      */
16891     indexOf : function(node){
16892         node = this.getNode(node);
16893         if(typeof node.nodeIndex == "number"){
16894             return node.nodeIndex;
16895         }
16896         var ns = this.nodes;
16897         for(var i = 0, len = ns.length; i < len; i++){
16898             if(ns[i] == node){
16899                 return i;
16900             }
16901         }
16902         return -1;
16903     }
16904 });
16905 /*
16906  * - LGPL
16907  *
16908  * based on jquery fullcalendar
16909  * 
16910  */
16911
16912 Roo.bootstrap = Roo.bootstrap || {};
16913 /**
16914  * @class Roo.bootstrap.Calendar
16915  * @extends Roo.bootstrap.Component
16916  * Bootstrap Calendar class
16917  * @cfg {Boolean} loadMask (true|false) default false
16918  * @cfg {Object} header generate the user specific header of the calendar, default false
16919
16920  * @constructor
16921  * Create a new Container
16922  * @param {Object} config The config object
16923  */
16924
16925
16926
16927 Roo.bootstrap.Calendar = function(config){
16928     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16929      this.addEvents({
16930         /**
16931              * @event select
16932              * Fires when a date is selected
16933              * @param {DatePicker} this
16934              * @param {Date} date The selected date
16935              */
16936         'select': true,
16937         /**
16938              * @event monthchange
16939              * Fires when the displayed month changes 
16940              * @param {DatePicker} this
16941              * @param {Date} date The selected month
16942              */
16943         'monthchange': true,
16944         /**
16945              * @event evententer
16946              * Fires when mouse over an event
16947              * @param {Calendar} this
16948              * @param {event} Event
16949              */
16950         'evententer': true,
16951         /**
16952              * @event eventleave
16953              * Fires when the mouse leaves an
16954              * @param {Calendar} this
16955              * @param {event}
16956              */
16957         'eventleave': true,
16958         /**
16959              * @event eventclick
16960              * Fires when the mouse click an
16961              * @param {Calendar} this
16962              * @param {event}
16963              */
16964         'eventclick': true
16965         
16966     });
16967
16968 };
16969
16970 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16971     
16972      /**
16973      * @cfg {Number} startDay
16974      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16975      */
16976     startDay : 0,
16977     
16978     loadMask : false,
16979     
16980     header : false,
16981       
16982     getAutoCreate : function(){
16983         
16984         
16985         var fc_button = function(name, corner, style, content ) {
16986             return Roo.apply({},{
16987                 tag : 'span',
16988                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16989                          (corner.length ?
16990                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16991                             ''
16992                         ),
16993                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16994                 unselectable: 'on'
16995             });
16996         };
16997         
16998         var header = {};
16999         
17000         if(!this.header){
17001             header = {
17002                 tag : 'table',
17003                 cls : 'fc-header',
17004                 style : 'width:100%',
17005                 cn : [
17006                     {
17007                         tag: 'tr',
17008                         cn : [
17009                             {
17010                                 tag : 'td',
17011                                 cls : 'fc-header-left',
17012                                 cn : [
17013                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
17014                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
17015                                     { tag: 'span', cls: 'fc-header-space' },
17016                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
17017
17018
17019                                 ]
17020                             },
17021
17022                             {
17023                                 tag : 'td',
17024                                 cls : 'fc-header-center',
17025                                 cn : [
17026                                     {
17027                                         tag: 'span',
17028                                         cls: 'fc-header-title',
17029                                         cn : {
17030                                             tag: 'H2',
17031                                             html : 'month / year'
17032                                         }
17033                                     }
17034
17035                                 ]
17036                             },
17037                             {
17038                                 tag : 'td',
17039                                 cls : 'fc-header-right',
17040                                 cn : [
17041                               /*      fc_button('month', 'left', '', 'month' ),
17042                                     fc_button('week', '', '', 'week' ),
17043                                     fc_button('day', 'right', '', 'day' )
17044                                 */    
17045
17046                                 ]
17047                             }
17048
17049                         ]
17050                     }
17051                 ]
17052             };
17053         }
17054         
17055         header = this.header;
17056         
17057        
17058         var cal_heads = function() {
17059             var ret = [];
17060             // fixme - handle this.
17061             
17062             for (var i =0; i < Date.dayNames.length; i++) {
17063                 var d = Date.dayNames[i];
17064                 ret.push({
17065                     tag: 'th',
17066                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17067                     html : d.substring(0,3)
17068                 });
17069                 
17070             }
17071             ret[0].cls += ' fc-first';
17072             ret[6].cls += ' fc-last';
17073             return ret;
17074         };
17075         var cal_cell = function(n) {
17076             return  {
17077                 tag: 'td',
17078                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17079                 cn : [
17080                     {
17081                         cn : [
17082                             {
17083                                 cls: 'fc-day-number',
17084                                 html: 'D'
17085                             },
17086                             {
17087                                 cls: 'fc-day-content',
17088                              
17089                                 cn : [
17090                                      {
17091                                         style: 'position: relative;' // height: 17px;
17092                                     }
17093                                 ]
17094                             }
17095                             
17096                             
17097                         ]
17098                     }
17099                 ]
17100                 
17101             }
17102         };
17103         var cal_rows = function() {
17104             
17105             var ret = [];
17106             for (var r = 0; r < 6; r++) {
17107                 var row= {
17108                     tag : 'tr',
17109                     cls : 'fc-week',
17110                     cn : []
17111                 };
17112                 
17113                 for (var i =0; i < Date.dayNames.length; i++) {
17114                     var d = Date.dayNames[i];
17115                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17116
17117                 }
17118                 row.cn[0].cls+=' fc-first';
17119                 row.cn[0].cn[0].style = 'min-height:90px';
17120                 row.cn[6].cls+=' fc-last';
17121                 ret.push(row);
17122                 
17123             }
17124             ret[0].cls += ' fc-first';
17125             ret[4].cls += ' fc-prev-last';
17126             ret[5].cls += ' fc-last';
17127             return ret;
17128             
17129         };
17130         
17131         var cal_table = {
17132             tag: 'table',
17133             cls: 'fc-border-separate',
17134             style : 'width:100%',
17135             cellspacing  : 0,
17136             cn : [
17137                 { 
17138                     tag: 'thead',
17139                     cn : [
17140                         { 
17141                             tag: 'tr',
17142                             cls : 'fc-first fc-last',
17143                             cn : cal_heads()
17144                         }
17145                     ]
17146                 },
17147                 { 
17148                     tag: 'tbody',
17149                     cn : cal_rows()
17150                 }
17151                   
17152             ]
17153         };
17154          
17155          var cfg = {
17156             cls : 'fc fc-ltr',
17157             cn : [
17158                 header,
17159                 {
17160                     cls : 'fc-content',
17161                     style : "position: relative;",
17162                     cn : [
17163                         {
17164                             cls : 'fc-view fc-view-month fc-grid',
17165                             style : 'position: relative',
17166                             unselectable : 'on',
17167                             cn : [
17168                                 {
17169                                     cls : 'fc-event-container',
17170                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17171                                 },
17172                                 cal_table
17173                             ]
17174                         }
17175                     ]
17176     
17177                 }
17178            ] 
17179             
17180         };
17181         
17182          
17183         
17184         return cfg;
17185     },
17186     
17187     
17188     initEvents : function()
17189     {
17190         if(!this.store){
17191             throw "can not find store for calendar";
17192         }
17193         
17194         var mark = {
17195             tag: "div",
17196             cls:"x-dlg-mask",
17197             style: "text-align:center",
17198             cn: [
17199                 {
17200                     tag: "div",
17201                     style: "background-color:white;width:50%;margin:250 auto",
17202                     cn: [
17203                         {
17204                             tag: "img",
17205                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17206                         },
17207                         {
17208                             tag: "span",
17209                             html: "Loading"
17210                         }
17211                         
17212                     ]
17213                 }
17214             ]
17215         };
17216         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17217         
17218         var size = this.el.select('.fc-content', true).first().getSize();
17219         this.maskEl.setSize(size.width, size.height);
17220         this.maskEl.enableDisplayMode("block");
17221         if(!this.loadMask){
17222             this.maskEl.hide();
17223         }
17224         
17225         this.store = Roo.factory(this.store, Roo.data);
17226         this.store.on('load', this.onLoad, this);
17227         this.store.on('beforeload', this.onBeforeLoad, this);
17228         
17229         this.resize();
17230         
17231         this.cells = this.el.select('.fc-day',true);
17232         //Roo.log(this.cells);
17233         this.textNodes = this.el.query('.fc-day-number');
17234         this.cells.addClassOnOver('fc-state-hover');
17235         
17236         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17237         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17238         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17239         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17240         
17241         this.on('monthchange', this.onMonthChange, this);
17242         
17243         this.update(new Date().clearTime());
17244     },
17245     
17246     resize : function() {
17247         var sz  = this.el.getSize();
17248         
17249         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17250         this.el.select('.fc-day-content div',true).setHeight(34);
17251     },
17252     
17253     
17254     // private
17255     showPrevMonth : function(e){
17256         this.update(this.activeDate.add("mo", -1));
17257     },
17258     showToday : function(e){
17259         this.update(new Date().clearTime());
17260     },
17261     // private
17262     showNextMonth : function(e){
17263         this.update(this.activeDate.add("mo", 1));
17264     },
17265
17266     // private
17267     showPrevYear : function(){
17268         this.update(this.activeDate.add("y", -1));
17269     },
17270
17271     // private
17272     showNextYear : function(){
17273         this.update(this.activeDate.add("y", 1));
17274     },
17275
17276     
17277    // private
17278     update : function(date)
17279     {
17280         var vd = this.activeDate;
17281         this.activeDate = date;
17282 //        if(vd && this.el){
17283 //            var t = date.getTime();
17284 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17285 //                Roo.log('using add remove');
17286 //                
17287 //                this.fireEvent('monthchange', this, date);
17288 //                
17289 //                this.cells.removeClass("fc-state-highlight");
17290 //                this.cells.each(function(c){
17291 //                   if(c.dateValue == t){
17292 //                       c.addClass("fc-state-highlight");
17293 //                       setTimeout(function(){
17294 //                            try{c.dom.firstChild.focus();}catch(e){}
17295 //                       }, 50);
17296 //                       return false;
17297 //                   }
17298 //                   return true;
17299 //                });
17300 //                return;
17301 //            }
17302 //        }
17303         
17304         var days = date.getDaysInMonth();
17305         
17306         var firstOfMonth = date.getFirstDateOfMonth();
17307         var startingPos = firstOfMonth.getDay()-this.startDay;
17308         
17309         if(startingPos < this.startDay){
17310             startingPos += 7;
17311         }
17312         
17313         var pm = date.add(Date.MONTH, -1);
17314         var prevStart = pm.getDaysInMonth()-startingPos;
17315 //        
17316         this.cells = this.el.select('.fc-day',true);
17317         this.textNodes = this.el.query('.fc-day-number');
17318         this.cells.addClassOnOver('fc-state-hover');
17319         
17320         var cells = this.cells.elements;
17321         var textEls = this.textNodes;
17322         
17323         Roo.each(cells, function(cell){
17324             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17325         });
17326         
17327         days += startingPos;
17328
17329         // convert everything to numbers so it's fast
17330         var day = 86400000;
17331         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17332         //Roo.log(d);
17333         //Roo.log(pm);
17334         //Roo.log(prevStart);
17335         
17336         var today = new Date().clearTime().getTime();
17337         var sel = date.clearTime().getTime();
17338         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17339         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17340         var ddMatch = this.disabledDatesRE;
17341         var ddText = this.disabledDatesText;
17342         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17343         var ddaysText = this.disabledDaysText;
17344         var format = this.format;
17345         
17346         var setCellClass = function(cal, cell){
17347             cell.row = 0;
17348             cell.events = [];
17349             cell.more = [];
17350             //Roo.log('set Cell Class');
17351             cell.title = "";
17352             var t = d.getTime();
17353             
17354             //Roo.log(d);
17355             
17356             cell.dateValue = t;
17357             if(t == today){
17358                 cell.className += " fc-today";
17359                 cell.className += " fc-state-highlight";
17360                 cell.title = cal.todayText;
17361             }
17362             if(t == sel){
17363                 // disable highlight in other month..
17364                 //cell.className += " fc-state-highlight";
17365                 
17366             }
17367             // disabling
17368             if(t < min) {
17369                 cell.className = " fc-state-disabled";
17370                 cell.title = cal.minText;
17371                 return;
17372             }
17373             if(t > max) {
17374                 cell.className = " fc-state-disabled";
17375                 cell.title = cal.maxText;
17376                 return;
17377             }
17378             if(ddays){
17379                 if(ddays.indexOf(d.getDay()) != -1){
17380                     cell.title = ddaysText;
17381                     cell.className = " fc-state-disabled";
17382                 }
17383             }
17384             if(ddMatch && format){
17385                 var fvalue = d.dateFormat(format);
17386                 if(ddMatch.test(fvalue)){
17387                     cell.title = ddText.replace("%0", fvalue);
17388                     cell.className = " fc-state-disabled";
17389                 }
17390             }
17391             
17392             if (!cell.initialClassName) {
17393                 cell.initialClassName = cell.dom.className;
17394             }
17395             
17396             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17397         };
17398
17399         var i = 0;
17400         
17401         for(; i < startingPos; i++) {
17402             textEls[i].innerHTML = (++prevStart);
17403             d.setDate(d.getDate()+1);
17404             
17405             cells[i].className = "fc-past fc-other-month";
17406             setCellClass(this, cells[i]);
17407         }
17408         
17409         var intDay = 0;
17410         
17411         for(; i < days; i++){
17412             intDay = i - startingPos + 1;
17413             textEls[i].innerHTML = (intDay);
17414             d.setDate(d.getDate()+1);
17415             
17416             cells[i].className = ''; // "x-date-active";
17417             setCellClass(this, cells[i]);
17418         }
17419         var extraDays = 0;
17420         
17421         for(; i < 42; i++) {
17422             textEls[i].innerHTML = (++extraDays);
17423             d.setDate(d.getDate()+1);
17424             
17425             cells[i].className = "fc-future fc-other-month";
17426             setCellClass(this, cells[i]);
17427         }
17428         
17429         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17430         
17431         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17432         
17433         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17434         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17435         
17436         if(totalRows != 6){
17437             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17438             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17439         }
17440         
17441         this.fireEvent('monthchange', this, date);
17442         
17443         
17444         /*
17445         if(!this.internalRender){
17446             var main = this.el.dom.firstChild;
17447             var w = main.offsetWidth;
17448             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17449             Roo.fly(main).setWidth(w);
17450             this.internalRender = true;
17451             // opera does not respect the auto grow header center column
17452             // then, after it gets a width opera refuses to recalculate
17453             // without a second pass
17454             if(Roo.isOpera && !this.secondPass){
17455                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17456                 this.secondPass = true;
17457                 this.update.defer(10, this, [date]);
17458             }
17459         }
17460         */
17461         
17462     },
17463     
17464     findCell : function(dt) {
17465         dt = dt.clearTime().getTime();
17466         var ret = false;
17467         this.cells.each(function(c){
17468             //Roo.log("check " +c.dateValue + '?=' + dt);
17469             if(c.dateValue == dt){
17470                 ret = c;
17471                 return false;
17472             }
17473             return true;
17474         });
17475         
17476         return ret;
17477     },
17478     
17479     findCells : function(ev) {
17480         var s = ev.start.clone().clearTime().getTime();
17481        // Roo.log(s);
17482         var e= ev.end.clone().clearTime().getTime();
17483        // Roo.log(e);
17484         var ret = [];
17485         this.cells.each(function(c){
17486              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17487             
17488             if(c.dateValue > e){
17489                 return ;
17490             }
17491             if(c.dateValue < s){
17492                 return ;
17493             }
17494             ret.push(c);
17495         });
17496         
17497         return ret;    
17498     },
17499     
17500 //    findBestRow: function(cells)
17501 //    {
17502 //        var ret = 0;
17503 //        
17504 //        for (var i =0 ; i < cells.length;i++) {
17505 //            ret  = Math.max(cells[i].rows || 0,ret);
17506 //        }
17507 //        return ret;
17508 //        
17509 //    },
17510     
17511     
17512     addItem : function(ev)
17513     {
17514         // look for vertical location slot in
17515         var cells = this.findCells(ev);
17516         
17517 //        ev.row = this.findBestRow(cells);
17518         
17519         // work out the location.
17520         
17521         var crow = false;
17522         var rows = [];
17523         for(var i =0; i < cells.length; i++) {
17524             
17525             cells[i].row = cells[0].row;
17526             
17527             if(i == 0){
17528                 cells[i].row = cells[i].row + 1;
17529             }
17530             
17531             if (!crow) {
17532                 crow = {
17533                     start : cells[i],
17534                     end :  cells[i]
17535                 };
17536                 continue;
17537             }
17538             if (crow.start.getY() == cells[i].getY()) {
17539                 // on same row.
17540                 crow.end = cells[i];
17541                 continue;
17542             }
17543             // different row.
17544             rows.push(crow);
17545             crow = {
17546                 start: cells[i],
17547                 end : cells[i]
17548             };
17549             
17550         }
17551         
17552         rows.push(crow);
17553         ev.els = [];
17554         ev.rows = rows;
17555         ev.cells = cells;
17556         
17557         cells[0].events.push(ev);
17558         
17559         this.calevents.push(ev);
17560     },
17561     
17562     clearEvents: function() {
17563         
17564         if(!this.calevents){
17565             return;
17566         }
17567         
17568         Roo.each(this.cells.elements, function(c){
17569             c.row = 0;
17570             c.events = [];
17571             c.more = [];
17572         });
17573         
17574         Roo.each(this.calevents, function(e) {
17575             Roo.each(e.els, function(el) {
17576                 el.un('mouseenter' ,this.onEventEnter, this);
17577                 el.un('mouseleave' ,this.onEventLeave, this);
17578                 el.remove();
17579             },this);
17580         },this);
17581         
17582         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17583             e.remove();
17584         });
17585         
17586     },
17587     
17588     renderEvents: function()
17589     {   
17590         var _this = this;
17591         
17592         this.cells.each(function(c) {
17593             
17594             if(c.row < 5){
17595                 return;
17596             }
17597             
17598             var ev = c.events;
17599             
17600             var r = 4;
17601             if(c.row != c.events.length){
17602                 r = 4 - (4 - (c.row - c.events.length));
17603             }
17604             
17605             c.events = ev.slice(0, r);
17606             c.more = ev.slice(r);
17607             
17608             if(c.more.length && c.more.length == 1){
17609                 c.events.push(c.more.pop());
17610             }
17611             
17612             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17613             
17614         });
17615             
17616         this.cells.each(function(c) {
17617             
17618             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17619             
17620             
17621             for (var e = 0; e < c.events.length; e++){
17622                 var ev = c.events[e];
17623                 var rows = ev.rows;
17624                 
17625                 for(var i = 0; i < rows.length; i++) {
17626                 
17627                     // how many rows should it span..
17628
17629                     var  cfg = {
17630                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17631                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17632
17633                         unselectable : "on",
17634                         cn : [
17635                             {
17636                                 cls: 'fc-event-inner',
17637                                 cn : [
17638     //                                {
17639     //                                  tag:'span',
17640     //                                  cls: 'fc-event-time',
17641     //                                  html : cells.length > 1 ? '' : ev.time
17642     //                                },
17643                                     {
17644                                       tag:'span',
17645                                       cls: 'fc-event-title',
17646                                       html : String.format('{0}', ev.title)
17647                                     }
17648
17649
17650                                 ]
17651                             },
17652                             {
17653                                 cls: 'ui-resizable-handle ui-resizable-e',
17654                                 html : '&nbsp;&nbsp;&nbsp'
17655                             }
17656
17657                         ]
17658                     };
17659
17660                     if (i == 0) {
17661                         cfg.cls += ' fc-event-start';
17662                     }
17663                     if ((i+1) == rows.length) {
17664                         cfg.cls += ' fc-event-end';
17665                     }
17666
17667                     var ctr = _this.el.select('.fc-event-container',true).first();
17668                     var cg = ctr.createChild(cfg);
17669
17670                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17671                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17672
17673                     var r = (c.more.length) ? 1 : 0;
17674                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17675                     cg.setWidth(ebox.right - sbox.x -2);
17676
17677                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17678                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17679                     cg.on('click', _this.onEventClick, _this, ev);
17680
17681                     ev.els.push(cg);
17682                     
17683                 }
17684                 
17685             }
17686             
17687             
17688             if(c.more.length){
17689                 var  cfg = {
17690                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17691                     style : 'position: absolute',
17692                     unselectable : "on",
17693                     cn : [
17694                         {
17695                             cls: 'fc-event-inner',
17696                             cn : [
17697                                 {
17698                                   tag:'span',
17699                                   cls: 'fc-event-title',
17700                                   html : 'More'
17701                                 }
17702
17703
17704                             ]
17705                         },
17706                         {
17707                             cls: 'ui-resizable-handle ui-resizable-e',
17708                             html : '&nbsp;&nbsp;&nbsp'
17709                         }
17710
17711                     ]
17712                 };
17713
17714                 var ctr = _this.el.select('.fc-event-container',true).first();
17715                 var cg = ctr.createChild(cfg);
17716
17717                 var sbox = c.select('.fc-day-content',true).first().getBox();
17718                 var ebox = c.select('.fc-day-content',true).first().getBox();
17719                 //Roo.log(cg);
17720                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17721                 cg.setWidth(ebox.right - sbox.x -2);
17722
17723                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17724                 
17725             }
17726             
17727         });
17728         
17729         
17730         
17731     },
17732     
17733     onEventEnter: function (e, el,event,d) {
17734         this.fireEvent('evententer', this, el, event);
17735     },
17736     
17737     onEventLeave: function (e, el,event,d) {
17738         this.fireEvent('eventleave', this, el, event);
17739     },
17740     
17741     onEventClick: function (e, el,event,d) {
17742         this.fireEvent('eventclick', this, el, event);
17743     },
17744     
17745     onMonthChange: function () {
17746         this.store.load();
17747     },
17748     
17749     onMoreEventClick: function(e, el, more)
17750     {
17751         var _this = this;
17752         
17753         this.calpopover.placement = 'right';
17754         this.calpopover.setTitle('More');
17755         
17756         this.calpopover.setContent('');
17757         
17758         var ctr = this.calpopover.el.select('.popover-content', true).first();
17759         
17760         Roo.each(more, function(m){
17761             var cfg = {
17762                 cls : 'fc-event-hori fc-event-draggable',
17763                 html : m.title
17764             };
17765             var cg = ctr.createChild(cfg);
17766             
17767             cg.on('click', _this.onEventClick, _this, m);
17768         });
17769         
17770         this.calpopover.show(el);
17771         
17772         
17773     },
17774     
17775     onLoad: function () 
17776     {   
17777         this.calevents = [];
17778         var cal = this;
17779         
17780         if(this.store.getCount() > 0){
17781             this.store.data.each(function(d){
17782                cal.addItem({
17783                     id : d.data.id,
17784                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17785                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17786                     time : d.data.start_time,
17787                     title : d.data.title,
17788                     description : d.data.description,
17789                     venue : d.data.venue
17790                 });
17791             });
17792         }
17793         
17794         this.renderEvents();
17795         
17796         if(this.calevents.length && this.loadMask){
17797             this.maskEl.hide();
17798         }
17799     },
17800     
17801     onBeforeLoad: function()
17802     {
17803         this.clearEvents();
17804         if(this.loadMask){
17805             this.maskEl.show();
17806         }
17807     }
17808 });
17809
17810  
17811  /*
17812  * - LGPL
17813  *
17814  * element
17815  * 
17816  */
17817
17818 /**
17819  * @class Roo.bootstrap.Popover
17820  * @extends Roo.bootstrap.Component
17821  * Bootstrap Popover class
17822  * @cfg {String} html contents of the popover   (or false to use children..)
17823  * @cfg {String} title of popover (or false to hide)
17824  * @cfg {String} placement how it is placed
17825  * @cfg {String} trigger click || hover (or false to trigger manually)
17826  * @cfg {String} over what (parent or false to trigger manually.)
17827  * @cfg {Number} delay - delay before showing
17828  
17829  * @constructor
17830  * Create a new Popover
17831  * @param {Object} config The config object
17832  */
17833
17834 Roo.bootstrap.Popover = function(config){
17835     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17836     
17837     this.addEvents({
17838         // raw events
17839          /**
17840          * @event show
17841          * After the popover show
17842          * 
17843          * @param {Roo.bootstrap.Popover} this
17844          */
17845         "show" : true,
17846         /**
17847          * @event hide
17848          * After the popover hide
17849          * 
17850          * @param {Roo.bootstrap.Popover} this
17851          */
17852         "hide" : true
17853     });
17854 };
17855
17856 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17857     
17858     title: 'Fill in a title',
17859     html: false,
17860     
17861     placement : 'right',
17862     trigger : 'hover', // hover
17863     
17864     delay : 0,
17865     
17866     over: 'parent',
17867     
17868     can_build_overlaid : false,
17869     
17870     getChildContainer : function()
17871     {
17872         return this.el.select('.popover-content',true).first();
17873     },
17874     
17875     getAutoCreate : function(){
17876          
17877         var cfg = {
17878            cls : 'popover roo-dynamic',
17879            style: 'display:block',
17880            cn : [
17881                 {
17882                     cls : 'arrow'
17883                 },
17884                 {
17885                     cls : 'popover-inner',
17886                     cn : [
17887                         {
17888                             tag: 'h3',
17889                             cls: 'popover-title popover-header',
17890                             html : this.title
17891                         },
17892                         {
17893                             cls : 'popover-content popover-body',
17894                             html : this.html
17895                         }
17896                     ]
17897                     
17898                 }
17899            ]
17900         };
17901         
17902         return cfg;
17903     },
17904     setTitle: function(str)
17905     {
17906         this.title = str;
17907         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17908     },
17909     setContent: function(str)
17910     {
17911         this.html = str;
17912         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17913     },
17914     // as it get's added to the bottom of the page.
17915     onRender : function(ct, position)
17916     {
17917         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17918         if(!this.el){
17919             var cfg = Roo.apply({},  this.getAutoCreate());
17920             cfg.id = Roo.id();
17921             
17922             if (this.cls) {
17923                 cfg.cls += ' ' + this.cls;
17924             }
17925             if (this.style) {
17926                 cfg.style = this.style;
17927             }
17928             //Roo.log("adding to ");
17929             this.el = Roo.get(document.body).createChild(cfg, position);
17930 //            Roo.log(this.el);
17931         }
17932         this.initEvents();
17933     },
17934     
17935     initEvents : function()
17936     {
17937         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17938         this.el.enableDisplayMode('block');
17939         this.el.hide();
17940         if (this.over === false) {
17941             return; 
17942         }
17943         if (this.triggers === false) {
17944             return;
17945         }
17946         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17947         var triggers = this.trigger ? this.trigger.split(' ') : [];
17948         Roo.each(triggers, function(trigger) {
17949         
17950             if (trigger == 'click') {
17951                 on_el.on('click', this.toggle, this);
17952             } else if (trigger != 'manual') {
17953                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17954                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17955       
17956                 on_el.on(eventIn  ,this.enter, this);
17957                 on_el.on(eventOut, this.leave, this);
17958             }
17959         }, this);
17960         
17961     },
17962     
17963     
17964     // private
17965     timeout : null,
17966     hoverState : null,
17967     
17968     toggle : function () {
17969         this.hoverState == 'in' ? this.leave() : this.enter();
17970     },
17971     
17972     enter : function () {
17973         
17974         clearTimeout(this.timeout);
17975     
17976         this.hoverState = 'in';
17977     
17978         if (!this.delay || !this.delay.show) {
17979             this.show();
17980             return;
17981         }
17982         var _t = this;
17983         this.timeout = setTimeout(function () {
17984             if (_t.hoverState == 'in') {
17985                 _t.show();
17986             }
17987         }, this.delay.show)
17988     },
17989     
17990     leave : function() {
17991         clearTimeout(this.timeout);
17992     
17993         this.hoverState = 'out';
17994     
17995         if (!this.delay || !this.delay.hide) {
17996             this.hide();
17997             return;
17998         }
17999         var _t = this;
18000         this.timeout = setTimeout(function () {
18001             if (_t.hoverState == 'out') {
18002                 _t.hide();
18003             }
18004         }, this.delay.hide)
18005     },
18006     
18007     show : function (on_el)
18008     {
18009         if (!on_el) {
18010             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18011         }
18012         
18013         // set content.
18014         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18015         if (this.html !== false) {
18016             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18017         }
18018         this.el.removeClass([
18019             'fade','top','bottom', 'left', 'right','in',
18020             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18021         ]);
18022         if (!this.title.length) {
18023             this.el.select('.popover-title',true).hide();
18024         }
18025         
18026         var placement = typeof this.placement == 'function' ?
18027             this.placement.call(this, this.el, on_el) :
18028             this.placement;
18029             
18030         var autoToken = /\s?auto?\s?/i;
18031         var autoPlace = autoToken.test(placement);
18032         if (autoPlace) {
18033             placement = placement.replace(autoToken, '') || 'top';
18034         }
18035         
18036         //this.el.detach()
18037         //this.el.setXY([0,0]);
18038         this.el.show();
18039         this.el.dom.style.display='block';
18040         this.el.addClass(placement);
18041         
18042         //this.el.appendTo(on_el);
18043         
18044         var p = this.getPosition();
18045         var box = this.el.getBox();
18046         
18047         if (autoPlace) {
18048             // fixme..
18049         }
18050         var align = Roo.bootstrap.Popover.alignment[placement];
18051         
18052 //        Roo.log(align);
18053         this.el.alignTo(on_el, align[0],align[1]);
18054         //var arrow = this.el.select('.arrow',true).first();
18055         //arrow.set(align[2], 
18056         
18057         this.el.addClass('in');
18058         
18059         
18060         if (this.el.hasClass('fade')) {
18061             // fade it?
18062         }
18063         
18064         this.hoverState = 'in';
18065         
18066         this.fireEvent('show', this);
18067         
18068     },
18069     hide : function()
18070     {
18071         this.el.setXY([0,0]);
18072         this.el.removeClass('in');
18073         this.el.hide();
18074         this.hoverState = null;
18075         
18076         this.fireEvent('hide', this);
18077     }
18078     
18079 });
18080
18081 Roo.bootstrap.Popover.alignment = {
18082     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18083     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18084     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18085     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18086 };
18087
18088  /*
18089  * - LGPL
18090  *
18091  * Progress
18092  * 
18093  */
18094
18095 /**
18096  * @class Roo.bootstrap.Progress
18097  * @extends Roo.bootstrap.Component
18098  * Bootstrap Progress class
18099  * @cfg {Boolean} striped striped of the progress bar
18100  * @cfg {Boolean} active animated of the progress bar
18101  * 
18102  * 
18103  * @constructor
18104  * Create a new Progress
18105  * @param {Object} config The config object
18106  */
18107
18108 Roo.bootstrap.Progress = function(config){
18109     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18110 };
18111
18112 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18113     
18114     striped : false,
18115     active: false,
18116     
18117     getAutoCreate : function(){
18118         var cfg = {
18119             tag: 'div',
18120             cls: 'progress'
18121         };
18122         
18123         
18124         if(this.striped){
18125             cfg.cls += ' progress-striped';
18126         }
18127       
18128         if(this.active){
18129             cfg.cls += ' active';
18130         }
18131         
18132         
18133         return cfg;
18134     }
18135    
18136 });
18137
18138  
18139
18140  /*
18141  * - LGPL
18142  *
18143  * ProgressBar
18144  * 
18145  */
18146
18147 /**
18148  * @class Roo.bootstrap.ProgressBar
18149  * @extends Roo.bootstrap.Component
18150  * Bootstrap ProgressBar class
18151  * @cfg {Number} aria_valuenow aria-value now
18152  * @cfg {Number} aria_valuemin aria-value min
18153  * @cfg {Number} aria_valuemax aria-value max
18154  * @cfg {String} label label for the progress bar
18155  * @cfg {String} panel (success | info | warning | danger )
18156  * @cfg {String} role role of the progress bar
18157  * @cfg {String} sr_only text
18158  * 
18159  * 
18160  * @constructor
18161  * Create a new ProgressBar
18162  * @param {Object} config The config object
18163  */
18164
18165 Roo.bootstrap.ProgressBar = function(config){
18166     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18167 };
18168
18169 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18170     
18171     aria_valuenow : 0,
18172     aria_valuemin : 0,
18173     aria_valuemax : 100,
18174     label : false,
18175     panel : false,
18176     role : false,
18177     sr_only: false,
18178     
18179     getAutoCreate : function()
18180     {
18181         
18182         var cfg = {
18183             tag: 'div',
18184             cls: 'progress-bar',
18185             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18186         };
18187         
18188         if(this.sr_only){
18189             cfg.cn = {
18190                 tag: 'span',
18191                 cls: 'sr-only',
18192                 html: this.sr_only
18193             }
18194         }
18195         
18196         if(this.role){
18197             cfg.role = this.role;
18198         }
18199         
18200         if(this.aria_valuenow){
18201             cfg['aria-valuenow'] = this.aria_valuenow;
18202         }
18203         
18204         if(this.aria_valuemin){
18205             cfg['aria-valuemin'] = this.aria_valuemin;
18206         }
18207         
18208         if(this.aria_valuemax){
18209             cfg['aria-valuemax'] = this.aria_valuemax;
18210         }
18211         
18212         if(this.label && !this.sr_only){
18213             cfg.html = this.label;
18214         }
18215         
18216         if(this.panel){
18217             cfg.cls += ' progress-bar-' + this.panel;
18218         }
18219         
18220         return cfg;
18221     },
18222     
18223     update : function(aria_valuenow)
18224     {
18225         this.aria_valuenow = aria_valuenow;
18226         
18227         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18228     }
18229    
18230 });
18231
18232  
18233
18234  /*
18235  * - LGPL
18236  *
18237  * column
18238  * 
18239  */
18240
18241 /**
18242  * @class Roo.bootstrap.TabGroup
18243  * @extends Roo.bootstrap.Column
18244  * Bootstrap Column class
18245  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18246  * @cfg {Boolean} carousel true to make the group behave like a carousel
18247  * @cfg {Boolean} bullets show bullets for the panels
18248  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18249  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18250  * @cfg {Boolean} showarrow (true|false) show arrow default true
18251  * 
18252  * @constructor
18253  * Create a new TabGroup
18254  * @param {Object} config The config object
18255  */
18256
18257 Roo.bootstrap.TabGroup = function(config){
18258     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18259     if (!this.navId) {
18260         this.navId = Roo.id();
18261     }
18262     this.tabs = [];
18263     Roo.bootstrap.TabGroup.register(this);
18264     
18265 };
18266
18267 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18268     
18269     carousel : false,
18270     transition : false,
18271     bullets : 0,
18272     timer : 0,
18273     autoslide : false,
18274     slideFn : false,
18275     slideOnTouch : false,
18276     showarrow : true,
18277     
18278     getAutoCreate : function()
18279     {
18280         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18281         
18282         cfg.cls += ' tab-content';
18283         
18284         if (this.carousel) {
18285             cfg.cls += ' carousel slide';
18286             
18287             cfg.cn = [{
18288                cls : 'carousel-inner',
18289                cn : []
18290             }];
18291         
18292             if(this.bullets  && !Roo.isTouch){
18293                 
18294                 var bullets = {
18295                     cls : 'carousel-bullets',
18296                     cn : []
18297                 };
18298                
18299                 if(this.bullets_cls){
18300                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18301                 }
18302                 
18303                 bullets.cn.push({
18304                     cls : 'clear'
18305                 });
18306                 
18307                 cfg.cn[0].cn.push(bullets);
18308             }
18309             
18310             if(this.showarrow){
18311                 cfg.cn[0].cn.push({
18312                     tag : 'div',
18313                     class : 'carousel-arrow',
18314                     cn : [
18315                         {
18316                             tag : 'div',
18317                             class : 'carousel-prev',
18318                             cn : [
18319                                 {
18320                                     tag : 'i',
18321                                     class : 'fa fa-chevron-left'
18322                                 }
18323                             ]
18324                         },
18325                         {
18326                             tag : 'div',
18327                             class : 'carousel-next',
18328                             cn : [
18329                                 {
18330                                     tag : 'i',
18331                                     class : 'fa fa-chevron-right'
18332                                 }
18333                             ]
18334                         }
18335                     ]
18336                 });
18337             }
18338             
18339         }
18340         
18341         return cfg;
18342     },
18343     
18344     initEvents:  function()
18345     {
18346 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18347 //            this.el.on("touchstart", this.onTouchStart, this);
18348 //        }
18349         
18350         if(this.autoslide){
18351             var _this = this;
18352             
18353             this.slideFn = window.setInterval(function() {
18354                 _this.showPanelNext();
18355             }, this.timer);
18356         }
18357         
18358         if(this.showarrow){
18359             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18360             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18361         }
18362         
18363         
18364     },
18365     
18366 //    onTouchStart : function(e, el, o)
18367 //    {
18368 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18369 //            return;
18370 //        }
18371 //        
18372 //        this.showPanelNext();
18373 //    },
18374     
18375     
18376     getChildContainer : function()
18377     {
18378         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18379     },
18380     
18381     /**
18382     * register a Navigation item
18383     * @param {Roo.bootstrap.NavItem} the navitem to add
18384     */
18385     register : function(item)
18386     {
18387         this.tabs.push( item);
18388         item.navId = this.navId; // not really needed..
18389         this.addBullet();
18390     
18391     },
18392     
18393     getActivePanel : function()
18394     {
18395         var r = false;
18396         Roo.each(this.tabs, function(t) {
18397             if (t.active) {
18398                 r = t;
18399                 return false;
18400             }
18401             return null;
18402         });
18403         return r;
18404         
18405     },
18406     getPanelByName : function(n)
18407     {
18408         var r = false;
18409         Roo.each(this.tabs, function(t) {
18410             if (t.tabId == n) {
18411                 r = t;
18412                 return false;
18413             }
18414             return null;
18415         });
18416         return r;
18417     },
18418     indexOfPanel : function(p)
18419     {
18420         var r = false;
18421         Roo.each(this.tabs, function(t,i) {
18422             if (t.tabId == p.tabId) {
18423                 r = i;
18424                 return false;
18425             }
18426             return null;
18427         });
18428         return r;
18429     },
18430     /**
18431      * show a specific panel
18432      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18433      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18434      */
18435     showPanel : function (pan)
18436     {
18437         if(this.transition || typeof(pan) == 'undefined'){
18438             Roo.log("waiting for the transitionend");
18439             return false;
18440         }
18441         
18442         if (typeof(pan) == 'number') {
18443             pan = this.tabs[pan];
18444         }
18445         
18446         if (typeof(pan) == 'string') {
18447             pan = this.getPanelByName(pan);
18448         }
18449         
18450         var cur = this.getActivePanel();
18451         
18452         if(!pan || !cur){
18453             Roo.log('pan or acitve pan is undefined');
18454             return false;
18455         }
18456         
18457         if (pan.tabId == this.getActivePanel().tabId) {
18458             return true;
18459         }
18460         
18461         if (false === cur.fireEvent('beforedeactivate')) {
18462             return false;
18463         }
18464         
18465         if(this.bullets > 0 && !Roo.isTouch){
18466             this.setActiveBullet(this.indexOfPanel(pan));
18467         }
18468         
18469         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18470             
18471             //class="carousel-item carousel-item-next carousel-item-left"
18472             
18473             this.transition = true;
18474             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18475             var lr = dir == 'next' ? 'left' : 'right';
18476             pan.el.addClass(dir); // or prev
18477             pan.el.addClass('carousel-item-' + dir); // or prev
18478             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18479             cur.el.addClass(lr); // or right
18480             pan.el.addClass(lr);
18481             cur.el.addClass('carousel-item-' +lr); // or right
18482             pan.el.addClass('carousel-item-' +lr);
18483             
18484             
18485             var _this = this;
18486             cur.el.on('transitionend', function() {
18487                 Roo.log("trans end?");
18488                 
18489                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18490                 pan.setActive(true);
18491                 
18492                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18493                 cur.setActive(false);
18494                 
18495                 _this.transition = false;
18496                 
18497             }, this, { single:  true } );
18498             
18499             return true;
18500         }
18501         
18502         cur.setActive(false);
18503         pan.setActive(true);
18504         
18505         return true;
18506         
18507     },
18508     showPanelNext : function()
18509     {
18510         var i = this.indexOfPanel(this.getActivePanel());
18511         
18512         if (i >= this.tabs.length - 1 && !this.autoslide) {
18513             return;
18514         }
18515         
18516         if (i >= this.tabs.length - 1 && this.autoslide) {
18517             i = -1;
18518         }
18519         
18520         this.showPanel(this.tabs[i+1]);
18521     },
18522     
18523     showPanelPrev : function()
18524     {
18525         var i = this.indexOfPanel(this.getActivePanel());
18526         
18527         if (i  < 1 && !this.autoslide) {
18528             return;
18529         }
18530         
18531         if (i < 1 && this.autoslide) {
18532             i = this.tabs.length;
18533         }
18534         
18535         this.showPanel(this.tabs[i-1]);
18536     },
18537     
18538     
18539     addBullet: function()
18540     {
18541         if(!this.bullets || Roo.isTouch){
18542             return;
18543         }
18544         var ctr = this.el.select('.carousel-bullets',true).first();
18545         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18546         var bullet = ctr.createChild({
18547             cls : 'bullet bullet-' + i
18548         },ctr.dom.lastChild);
18549         
18550         
18551         var _this = this;
18552         
18553         bullet.on('click', (function(e, el, o, ii, t){
18554
18555             e.preventDefault();
18556
18557             this.showPanel(ii);
18558
18559             if(this.autoslide && this.slideFn){
18560                 clearInterval(this.slideFn);
18561                 this.slideFn = window.setInterval(function() {
18562                     _this.showPanelNext();
18563                 }, this.timer);
18564             }
18565
18566         }).createDelegate(this, [i, bullet], true));
18567                 
18568         
18569     },
18570      
18571     setActiveBullet : function(i)
18572     {
18573         if(Roo.isTouch){
18574             return;
18575         }
18576         
18577         Roo.each(this.el.select('.bullet', true).elements, function(el){
18578             el.removeClass('selected');
18579         });
18580
18581         var bullet = this.el.select('.bullet-' + i, true).first();
18582         
18583         if(!bullet){
18584             return;
18585         }
18586         
18587         bullet.addClass('selected');
18588     }
18589     
18590     
18591   
18592 });
18593
18594  
18595
18596  
18597  
18598 Roo.apply(Roo.bootstrap.TabGroup, {
18599     
18600     groups: {},
18601      /**
18602     * register a Navigation Group
18603     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18604     */
18605     register : function(navgrp)
18606     {
18607         this.groups[navgrp.navId] = navgrp;
18608         
18609     },
18610     /**
18611     * fetch a Navigation Group based on the navigation ID
18612     * if one does not exist , it will get created.
18613     * @param {string} the navgroup to add
18614     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18615     */
18616     get: function(navId) {
18617         if (typeof(this.groups[navId]) == 'undefined') {
18618             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18619         }
18620         return this.groups[navId] ;
18621     }
18622     
18623     
18624     
18625 });
18626
18627  /*
18628  * - LGPL
18629  *
18630  * TabPanel
18631  * 
18632  */
18633
18634 /**
18635  * @class Roo.bootstrap.TabPanel
18636  * @extends Roo.bootstrap.Component
18637  * Bootstrap TabPanel class
18638  * @cfg {Boolean} active panel active
18639  * @cfg {String} html panel content
18640  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18641  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18642  * @cfg {String} href click to link..
18643  * 
18644  * 
18645  * @constructor
18646  * Create a new TabPanel
18647  * @param {Object} config The config object
18648  */
18649
18650 Roo.bootstrap.TabPanel = function(config){
18651     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18652     this.addEvents({
18653         /**
18654              * @event changed
18655              * Fires when the active status changes
18656              * @param {Roo.bootstrap.TabPanel} this
18657              * @param {Boolean} state the new state
18658             
18659          */
18660         'changed': true,
18661         /**
18662              * @event beforedeactivate
18663              * Fires before a tab is de-activated - can be used to do validation on a form.
18664              * @param {Roo.bootstrap.TabPanel} this
18665              * @return {Boolean} false if there is an error
18666             
18667          */
18668         'beforedeactivate': true
18669      });
18670     
18671     this.tabId = this.tabId || Roo.id();
18672   
18673 };
18674
18675 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18676     
18677     active: false,
18678     html: false,
18679     tabId: false,
18680     navId : false,
18681     href : '',
18682     
18683     getAutoCreate : function(){
18684         
18685         
18686         var cfg = {
18687             tag: 'div',
18688             // item is needed for carousel - not sure if it has any effect otherwise
18689             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18690             html: this.html || ''
18691         };
18692         
18693         if(this.active){
18694             cfg.cls += ' active';
18695         }
18696         
18697         if(this.tabId){
18698             cfg.tabId = this.tabId;
18699         }
18700         
18701         
18702         
18703         return cfg;
18704     },
18705     
18706     initEvents:  function()
18707     {
18708         var p = this.parent();
18709         
18710         this.navId = this.navId || p.navId;
18711         
18712         if (typeof(this.navId) != 'undefined') {
18713             // not really needed.. but just in case.. parent should be a NavGroup.
18714             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18715             
18716             tg.register(this);
18717             
18718             var i = tg.tabs.length - 1;
18719             
18720             if(this.active && tg.bullets > 0 && i < tg.bullets){
18721                 tg.setActiveBullet(i);
18722             }
18723         }
18724         
18725         this.el.on('click', this.onClick, this);
18726         
18727         if(Roo.isTouch){
18728             this.el.on("touchstart", this.onTouchStart, this);
18729             this.el.on("touchmove", this.onTouchMove, this);
18730             this.el.on("touchend", this.onTouchEnd, this);
18731         }
18732         
18733     },
18734     
18735     onRender : function(ct, position)
18736     {
18737         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18738     },
18739     
18740     setActive : function(state)
18741     {
18742         Roo.log("panel - set active " + this.tabId + "=" + state);
18743         
18744         this.active = state;
18745         if (!state) {
18746             this.el.removeClass('active');
18747             
18748         } else  if (!this.el.hasClass('active')) {
18749             this.el.addClass('active');
18750         }
18751         
18752         this.fireEvent('changed', this, state);
18753     },
18754     
18755     onClick : function(e)
18756     {
18757         e.preventDefault();
18758         
18759         if(!this.href.length){
18760             return;
18761         }
18762         
18763         window.location.href = this.href;
18764     },
18765     
18766     startX : 0,
18767     startY : 0,
18768     endX : 0,
18769     endY : 0,
18770     swiping : false,
18771     
18772     onTouchStart : function(e)
18773     {
18774         this.swiping = false;
18775         
18776         this.startX = e.browserEvent.touches[0].clientX;
18777         this.startY = e.browserEvent.touches[0].clientY;
18778     },
18779     
18780     onTouchMove : function(e)
18781     {
18782         this.swiping = true;
18783         
18784         this.endX = e.browserEvent.touches[0].clientX;
18785         this.endY = e.browserEvent.touches[0].clientY;
18786     },
18787     
18788     onTouchEnd : function(e)
18789     {
18790         if(!this.swiping){
18791             this.onClick(e);
18792             return;
18793         }
18794         
18795         var tabGroup = this.parent();
18796         
18797         if(this.endX > this.startX){ // swiping right
18798             tabGroup.showPanelPrev();
18799             return;
18800         }
18801         
18802         if(this.startX > this.endX){ // swiping left
18803             tabGroup.showPanelNext();
18804             return;
18805         }
18806     }
18807     
18808     
18809 });
18810  
18811
18812  
18813
18814  /*
18815  * - LGPL
18816  *
18817  * DateField
18818  * 
18819  */
18820
18821 /**
18822  * @class Roo.bootstrap.DateField
18823  * @extends Roo.bootstrap.Input
18824  * Bootstrap DateField class
18825  * @cfg {Number} weekStart default 0
18826  * @cfg {String} viewMode default empty, (months|years)
18827  * @cfg {String} minViewMode default empty, (months|years)
18828  * @cfg {Number} startDate default -Infinity
18829  * @cfg {Number} endDate default Infinity
18830  * @cfg {Boolean} todayHighlight default false
18831  * @cfg {Boolean} todayBtn default false
18832  * @cfg {Boolean} calendarWeeks default false
18833  * @cfg {Object} daysOfWeekDisabled default empty
18834  * @cfg {Boolean} singleMode default false (true | false)
18835  * 
18836  * @cfg {Boolean} keyboardNavigation default true
18837  * @cfg {String} language default en
18838  * 
18839  * @constructor
18840  * Create a new DateField
18841  * @param {Object} config The config object
18842  */
18843
18844 Roo.bootstrap.DateField = function(config){
18845     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18846      this.addEvents({
18847             /**
18848              * @event show
18849              * Fires when this field show.
18850              * @param {Roo.bootstrap.DateField} this
18851              * @param {Mixed} date The date value
18852              */
18853             show : true,
18854             /**
18855              * @event show
18856              * Fires when this field hide.
18857              * @param {Roo.bootstrap.DateField} this
18858              * @param {Mixed} date The date value
18859              */
18860             hide : true,
18861             /**
18862              * @event select
18863              * Fires when select a date.
18864              * @param {Roo.bootstrap.DateField} this
18865              * @param {Mixed} date The date value
18866              */
18867             select : true,
18868             /**
18869              * @event beforeselect
18870              * Fires when before select a date.
18871              * @param {Roo.bootstrap.DateField} this
18872              * @param {Mixed} date The date value
18873              */
18874             beforeselect : true
18875         });
18876 };
18877
18878 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18879     
18880     /**
18881      * @cfg {String} format
18882      * The default date format string which can be overriden for localization support.  The format must be
18883      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18884      */
18885     format : "m/d/y",
18886     /**
18887      * @cfg {String} altFormats
18888      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18889      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18890      */
18891     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18892     
18893     weekStart : 0,
18894     
18895     viewMode : '',
18896     
18897     minViewMode : '',
18898     
18899     todayHighlight : false,
18900     
18901     todayBtn: false,
18902     
18903     language: 'en',
18904     
18905     keyboardNavigation: true,
18906     
18907     calendarWeeks: false,
18908     
18909     startDate: -Infinity,
18910     
18911     endDate: Infinity,
18912     
18913     daysOfWeekDisabled: [],
18914     
18915     _events: [],
18916     
18917     singleMode : false,
18918     
18919     UTCDate: function()
18920     {
18921         return new Date(Date.UTC.apply(Date, arguments));
18922     },
18923     
18924     UTCToday: function()
18925     {
18926         var today = new Date();
18927         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18928     },
18929     
18930     getDate: function() {
18931             var d = this.getUTCDate();
18932             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18933     },
18934     
18935     getUTCDate: function() {
18936             return this.date;
18937     },
18938     
18939     setDate: function(d) {
18940             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18941     },
18942     
18943     setUTCDate: function(d) {
18944             this.date = d;
18945             this.setValue(this.formatDate(this.date));
18946     },
18947         
18948     onRender: function(ct, position)
18949     {
18950         
18951         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18952         
18953         this.language = this.language || 'en';
18954         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18955         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18956         
18957         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18958         this.format = this.format || 'm/d/y';
18959         this.isInline = false;
18960         this.isInput = true;
18961         this.component = this.el.select('.add-on', true).first() || false;
18962         this.component = (this.component && this.component.length === 0) ? false : this.component;
18963         this.hasInput = this.component && this.inputEl().length;
18964         
18965         if (typeof(this.minViewMode === 'string')) {
18966             switch (this.minViewMode) {
18967                 case 'months':
18968                     this.minViewMode = 1;
18969                     break;
18970                 case 'years':
18971                     this.minViewMode = 2;
18972                     break;
18973                 default:
18974                     this.minViewMode = 0;
18975                     break;
18976             }
18977         }
18978         
18979         if (typeof(this.viewMode === 'string')) {
18980             switch (this.viewMode) {
18981                 case 'months':
18982                     this.viewMode = 1;
18983                     break;
18984                 case 'years':
18985                     this.viewMode = 2;
18986                     break;
18987                 default:
18988                     this.viewMode = 0;
18989                     break;
18990             }
18991         }
18992                 
18993         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18994         
18995 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18996         
18997         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18998         
18999         this.picker().on('mousedown', this.onMousedown, this);
19000         this.picker().on('click', this.onClick, this);
19001         
19002         this.picker().addClass('datepicker-dropdown');
19003         
19004         this.startViewMode = this.viewMode;
19005         
19006         if(this.singleMode){
19007             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19008                 v.setVisibilityMode(Roo.Element.DISPLAY);
19009                 v.hide();
19010             });
19011             
19012             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19013                 v.setStyle('width', '189px');
19014             });
19015         }
19016         
19017         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19018             if(!this.calendarWeeks){
19019                 v.remove();
19020                 return;
19021             }
19022             
19023             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19024             v.attr('colspan', function(i, val){
19025                 return parseInt(val) + 1;
19026             });
19027         });
19028                         
19029         
19030         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19031         
19032         this.setStartDate(this.startDate);
19033         this.setEndDate(this.endDate);
19034         
19035         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19036         
19037         this.fillDow();
19038         this.fillMonths();
19039         this.update();
19040         this.showMode();
19041         
19042         if(this.isInline) {
19043             this.showPopup();
19044         }
19045     },
19046     
19047     picker : function()
19048     {
19049         return this.pickerEl;
19050 //        return this.el.select('.datepicker', true).first();
19051     },
19052     
19053     fillDow: function()
19054     {
19055         var dowCnt = this.weekStart;
19056         
19057         var dow = {
19058             tag: 'tr',
19059             cn: [
19060                 
19061             ]
19062         };
19063         
19064         if(this.calendarWeeks){
19065             dow.cn.push({
19066                 tag: 'th',
19067                 cls: 'cw',
19068                 html: '&nbsp;'
19069             })
19070         }
19071         
19072         while (dowCnt < this.weekStart + 7) {
19073             dow.cn.push({
19074                 tag: 'th',
19075                 cls: 'dow',
19076                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19077             });
19078         }
19079         
19080         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19081     },
19082     
19083     fillMonths: function()
19084     {    
19085         var i = 0;
19086         var months = this.picker().select('>.datepicker-months td', true).first();
19087         
19088         months.dom.innerHTML = '';
19089         
19090         while (i < 12) {
19091             var month = {
19092                 tag: 'span',
19093                 cls: 'month',
19094                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19095             };
19096             
19097             months.createChild(month);
19098         }
19099         
19100     },
19101     
19102     update: function()
19103     {
19104         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;
19105         
19106         if (this.date < this.startDate) {
19107             this.viewDate = new Date(this.startDate);
19108         } else if (this.date > this.endDate) {
19109             this.viewDate = new Date(this.endDate);
19110         } else {
19111             this.viewDate = new Date(this.date);
19112         }
19113         
19114         this.fill();
19115     },
19116     
19117     fill: function() 
19118     {
19119         var d = new Date(this.viewDate),
19120                 year = d.getUTCFullYear(),
19121                 month = d.getUTCMonth(),
19122                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19123                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19124                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19125                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19126                 currentDate = this.date && this.date.valueOf(),
19127                 today = this.UTCToday();
19128         
19129         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19130         
19131 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19132         
19133 //        this.picker.select('>tfoot th.today').
19134 //                                              .text(dates[this.language].today)
19135 //                                              .toggle(this.todayBtn !== false);
19136     
19137         this.updateNavArrows();
19138         this.fillMonths();
19139                                                 
19140         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19141         
19142         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19143          
19144         prevMonth.setUTCDate(day);
19145         
19146         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19147         
19148         var nextMonth = new Date(prevMonth);
19149         
19150         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19151         
19152         nextMonth = nextMonth.valueOf();
19153         
19154         var fillMonths = false;
19155         
19156         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19157         
19158         while(prevMonth.valueOf() <= nextMonth) {
19159             var clsName = '';
19160             
19161             if (prevMonth.getUTCDay() === this.weekStart) {
19162                 if(fillMonths){
19163                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19164                 }
19165                     
19166                 fillMonths = {
19167                     tag: 'tr',
19168                     cn: []
19169                 };
19170                 
19171                 if(this.calendarWeeks){
19172                     // ISO 8601: First week contains first thursday.
19173                     // ISO also states week starts on Monday, but we can be more abstract here.
19174                     var
19175                     // Start of current week: based on weekstart/current date
19176                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19177                     // Thursday of this week
19178                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19179                     // First Thursday of year, year from thursday
19180                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19181                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19182                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19183                     
19184                     fillMonths.cn.push({
19185                         tag: 'td',
19186                         cls: 'cw',
19187                         html: calWeek
19188                     });
19189                 }
19190             }
19191             
19192             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19193                 clsName += ' old';
19194             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19195                 clsName += ' new';
19196             }
19197             if (this.todayHighlight &&
19198                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19199                 prevMonth.getUTCMonth() == today.getMonth() &&
19200                 prevMonth.getUTCDate() == today.getDate()) {
19201                 clsName += ' today';
19202             }
19203             
19204             if (currentDate && prevMonth.valueOf() === currentDate) {
19205                 clsName += ' active';
19206             }
19207             
19208             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19209                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19210                     clsName += ' disabled';
19211             }
19212             
19213             fillMonths.cn.push({
19214                 tag: 'td',
19215                 cls: 'day ' + clsName,
19216                 html: prevMonth.getDate()
19217             });
19218             
19219             prevMonth.setDate(prevMonth.getDate()+1);
19220         }
19221           
19222         var currentYear = this.date && this.date.getUTCFullYear();
19223         var currentMonth = this.date && this.date.getUTCMonth();
19224         
19225         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19226         
19227         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19228             v.removeClass('active');
19229             
19230             if(currentYear === year && k === currentMonth){
19231                 v.addClass('active');
19232             }
19233             
19234             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19235                 v.addClass('disabled');
19236             }
19237             
19238         });
19239         
19240         
19241         year = parseInt(year/10, 10) * 10;
19242         
19243         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19244         
19245         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19246         
19247         year -= 1;
19248         for (var i = -1; i < 11; i++) {
19249             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19250                 tag: 'span',
19251                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19252                 html: year
19253             });
19254             
19255             year += 1;
19256         }
19257     },
19258     
19259     showMode: function(dir) 
19260     {
19261         if (dir) {
19262             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19263         }
19264         
19265         Roo.each(this.picker().select('>div',true).elements, function(v){
19266             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19267             v.hide();
19268         });
19269         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19270     },
19271     
19272     place: function()
19273     {
19274         if(this.isInline) {
19275             return;
19276         }
19277         
19278         this.picker().removeClass(['bottom', 'top']);
19279         
19280         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19281             /*
19282              * place to the top of element!
19283              *
19284              */
19285             
19286             this.picker().addClass('top');
19287             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19288             
19289             return;
19290         }
19291         
19292         this.picker().addClass('bottom');
19293         
19294         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19295     },
19296     
19297     parseDate : function(value)
19298     {
19299         if(!value || value instanceof Date){
19300             return value;
19301         }
19302         var v = Date.parseDate(value, this.format);
19303         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19304             v = Date.parseDate(value, 'Y-m-d');
19305         }
19306         if(!v && this.altFormats){
19307             if(!this.altFormatsArray){
19308                 this.altFormatsArray = this.altFormats.split("|");
19309             }
19310             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19311                 v = Date.parseDate(value, this.altFormatsArray[i]);
19312             }
19313         }
19314         return v;
19315     },
19316     
19317     formatDate : function(date, fmt)
19318     {   
19319         return (!date || !(date instanceof Date)) ?
19320         date : date.dateFormat(fmt || this.format);
19321     },
19322     
19323     onFocus : function()
19324     {
19325         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19326         this.showPopup();
19327     },
19328     
19329     onBlur : function()
19330     {
19331         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19332         
19333         var d = this.inputEl().getValue();
19334         
19335         this.setValue(d);
19336                 
19337         this.hidePopup();
19338     },
19339     
19340     showPopup : function()
19341     {
19342         this.picker().show();
19343         this.update();
19344         this.place();
19345         
19346         this.fireEvent('showpopup', this, this.date);
19347     },
19348     
19349     hidePopup : function()
19350     {
19351         if(this.isInline) {
19352             return;
19353         }
19354         this.picker().hide();
19355         this.viewMode = this.startViewMode;
19356         this.showMode();
19357         
19358         this.fireEvent('hidepopup', this, this.date);
19359         
19360     },
19361     
19362     onMousedown: function(e)
19363     {
19364         e.stopPropagation();
19365         e.preventDefault();
19366     },
19367     
19368     keyup: function(e)
19369     {
19370         Roo.bootstrap.DateField.superclass.keyup.call(this);
19371         this.update();
19372     },
19373
19374     setValue: function(v)
19375     {
19376         if(this.fireEvent('beforeselect', this, v) !== false){
19377             var d = new Date(this.parseDate(v) ).clearTime();
19378         
19379             if(isNaN(d.getTime())){
19380                 this.date = this.viewDate = '';
19381                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19382                 return;
19383             }
19384
19385             v = this.formatDate(d);
19386
19387             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19388
19389             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19390
19391             this.update();
19392
19393             this.fireEvent('select', this, this.date);
19394         }
19395     },
19396     
19397     getValue: function()
19398     {
19399         return this.formatDate(this.date);
19400     },
19401     
19402     fireKey: function(e)
19403     {
19404         if (!this.picker().isVisible()){
19405             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19406                 this.showPopup();
19407             }
19408             return;
19409         }
19410         
19411         var dateChanged = false,
19412         dir, day, month,
19413         newDate, newViewDate;
19414         
19415         switch(e.keyCode){
19416             case 27: // escape
19417                 this.hidePopup();
19418                 e.preventDefault();
19419                 break;
19420             case 37: // left
19421             case 39: // right
19422                 if (!this.keyboardNavigation) {
19423                     break;
19424                 }
19425                 dir = e.keyCode == 37 ? -1 : 1;
19426                 
19427                 if (e.ctrlKey){
19428                     newDate = this.moveYear(this.date, dir);
19429                     newViewDate = this.moveYear(this.viewDate, dir);
19430                 } else if (e.shiftKey){
19431                     newDate = this.moveMonth(this.date, dir);
19432                     newViewDate = this.moveMonth(this.viewDate, dir);
19433                 } else {
19434                     newDate = new Date(this.date);
19435                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19436                     newViewDate = new Date(this.viewDate);
19437                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19438                 }
19439                 if (this.dateWithinRange(newDate)){
19440                     this.date = newDate;
19441                     this.viewDate = newViewDate;
19442                     this.setValue(this.formatDate(this.date));
19443 //                    this.update();
19444                     e.preventDefault();
19445                     dateChanged = true;
19446                 }
19447                 break;
19448             case 38: // up
19449             case 40: // down
19450                 if (!this.keyboardNavigation) {
19451                     break;
19452                 }
19453                 dir = e.keyCode == 38 ? -1 : 1;
19454                 if (e.ctrlKey){
19455                     newDate = this.moveYear(this.date, dir);
19456                     newViewDate = this.moveYear(this.viewDate, dir);
19457                 } else if (e.shiftKey){
19458                     newDate = this.moveMonth(this.date, dir);
19459                     newViewDate = this.moveMonth(this.viewDate, dir);
19460                 } else {
19461                     newDate = new Date(this.date);
19462                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19463                     newViewDate = new Date(this.viewDate);
19464                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19465                 }
19466                 if (this.dateWithinRange(newDate)){
19467                     this.date = newDate;
19468                     this.viewDate = newViewDate;
19469                     this.setValue(this.formatDate(this.date));
19470 //                    this.update();
19471                     e.preventDefault();
19472                     dateChanged = true;
19473                 }
19474                 break;
19475             case 13: // enter
19476                 this.setValue(this.formatDate(this.date));
19477                 this.hidePopup();
19478                 e.preventDefault();
19479                 break;
19480             case 9: // tab
19481                 this.setValue(this.formatDate(this.date));
19482                 this.hidePopup();
19483                 break;
19484             case 16: // shift
19485             case 17: // ctrl
19486             case 18: // alt
19487                 break;
19488             default :
19489                 this.hidePopup();
19490                 
19491         }
19492     },
19493     
19494     
19495     onClick: function(e) 
19496     {
19497         e.stopPropagation();
19498         e.preventDefault();
19499         
19500         var target = e.getTarget();
19501         
19502         if(target.nodeName.toLowerCase() === 'i'){
19503             target = Roo.get(target).dom.parentNode;
19504         }
19505         
19506         var nodeName = target.nodeName;
19507         var className = target.className;
19508         var html = target.innerHTML;
19509         //Roo.log(nodeName);
19510         
19511         switch(nodeName.toLowerCase()) {
19512             case 'th':
19513                 switch(className) {
19514                     case 'switch':
19515                         this.showMode(1);
19516                         break;
19517                     case 'prev':
19518                     case 'next':
19519                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19520                         switch(this.viewMode){
19521                                 case 0:
19522                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19523                                         break;
19524                                 case 1:
19525                                 case 2:
19526                                         this.viewDate = this.moveYear(this.viewDate, dir);
19527                                         break;
19528                         }
19529                         this.fill();
19530                         break;
19531                     case 'today':
19532                         var date = new Date();
19533                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19534 //                        this.fill()
19535                         this.setValue(this.formatDate(this.date));
19536                         
19537                         this.hidePopup();
19538                         break;
19539                 }
19540                 break;
19541             case 'span':
19542                 if (className.indexOf('disabled') < 0) {
19543                     this.viewDate.setUTCDate(1);
19544                     if (className.indexOf('month') > -1) {
19545                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19546                     } else {
19547                         var year = parseInt(html, 10) || 0;
19548                         this.viewDate.setUTCFullYear(year);
19549                         
19550                     }
19551                     
19552                     if(this.singleMode){
19553                         this.setValue(this.formatDate(this.viewDate));
19554                         this.hidePopup();
19555                         return;
19556                     }
19557                     
19558                     this.showMode(-1);
19559                     this.fill();
19560                 }
19561                 break;
19562                 
19563             case 'td':
19564                 //Roo.log(className);
19565                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19566                     var day = parseInt(html, 10) || 1;
19567                     var year = this.viewDate.getUTCFullYear(),
19568                         month = this.viewDate.getUTCMonth();
19569
19570                     if (className.indexOf('old') > -1) {
19571                         if(month === 0 ){
19572                             month = 11;
19573                             year -= 1;
19574                         }else{
19575                             month -= 1;
19576                         }
19577                     } else if (className.indexOf('new') > -1) {
19578                         if (month == 11) {
19579                             month = 0;
19580                             year += 1;
19581                         } else {
19582                             month += 1;
19583                         }
19584                     }
19585                     //Roo.log([year,month,day]);
19586                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19587                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19588 //                    this.fill();
19589                     //Roo.log(this.formatDate(this.date));
19590                     this.setValue(this.formatDate(this.date));
19591                     this.hidePopup();
19592                 }
19593                 break;
19594         }
19595     },
19596     
19597     setStartDate: function(startDate)
19598     {
19599         this.startDate = startDate || -Infinity;
19600         if (this.startDate !== -Infinity) {
19601             this.startDate = this.parseDate(this.startDate);
19602         }
19603         this.update();
19604         this.updateNavArrows();
19605     },
19606
19607     setEndDate: function(endDate)
19608     {
19609         this.endDate = endDate || Infinity;
19610         if (this.endDate !== Infinity) {
19611             this.endDate = this.parseDate(this.endDate);
19612         }
19613         this.update();
19614         this.updateNavArrows();
19615     },
19616     
19617     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19618     {
19619         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19620         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19621             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19622         }
19623         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19624             return parseInt(d, 10);
19625         });
19626         this.update();
19627         this.updateNavArrows();
19628     },
19629     
19630     updateNavArrows: function() 
19631     {
19632         if(this.singleMode){
19633             return;
19634         }
19635         
19636         var d = new Date(this.viewDate),
19637         year = d.getUTCFullYear(),
19638         month = d.getUTCMonth();
19639         
19640         Roo.each(this.picker().select('.prev', true).elements, function(v){
19641             v.show();
19642             switch (this.viewMode) {
19643                 case 0:
19644
19645                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19646                         v.hide();
19647                     }
19648                     break;
19649                 case 1:
19650                 case 2:
19651                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19652                         v.hide();
19653                     }
19654                     break;
19655             }
19656         });
19657         
19658         Roo.each(this.picker().select('.next', true).elements, function(v){
19659             v.show();
19660             switch (this.viewMode) {
19661                 case 0:
19662
19663                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19664                         v.hide();
19665                     }
19666                     break;
19667                 case 1:
19668                 case 2:
19669                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19670                         v.hide();
19671                     }
19672                     break;
19673             }
19674         })
19675     },
19676     
19677     moveMonth: function(date, dir)
19678     {
19679         if (!dir) {
19680             return date;
19681         }
19682         var new_date = new Date(date.valueOf()),
19683         day = new_date.getUTCDate(),
19684         month = new_date.getUTCMonth(),
19685         mag = Math.abs(dir),
19686         new_month, test;
19687         dir = dir > 0 ? 1 : -1;
19688         if (mag == 1){
19689             test = dir == -1
19690             // If going back one month, make sure month is not current month
19691             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19692             ? function(){
19693                 return new_date.getUTCMonth() == month;
19694             }
19695             // If going forward one month, make sure month is as expected
19696             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19697             : function(){
19698                 return new_date.getUTCMonth() != new_month;
19699             };
19700             new_month = month + dir;
19701             new_date.setUTCMonth(new_month);
19702             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19703             if (new_month < 0 || new_month > 11) {
19704                 new_month = (new_month + 12) % 12;
19705             }
19706         } else {
19707             // For magnitudes >1, move one month at a time...
19708             for (var i=0; i<mag; i++) {
19709                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19710                 new_date = this.moveMonth(new_date, dir);
19711             }
19712             // ...then reset the day, keeping it in the new month
19713             new_month = new_date.getUTCMonth();
19714             new_date.setUTCDate(day);
19715             test = function(){
19716                 return new_month != new_date.getUTCMonth();
19717             };
19718         }
19719         // Common date-resetting loop -- if date is beyond end of month, make it
19720         // end of month
19721         while (test()){
19722             new_date.setUTCDate(--day);
19723             new_date.setUTCMonth(new_month);
19724         }
19725         return new_date;
19726     },
19727
19728     moveYear: function(date, dir)
19729     {
19730         return this.moveMonth(date, dir*12);
19731     },
19732
19733     dateWithinRange: function(date)
19734     {
19735         return date >= this.startDate && date <= this.endDate;
19736     },
19737
19738     
19739     remove: function() 
19740     {
19741         this.picker().remove();
19742     },
19743     
19744     validateValue : function(value)
19745     {
19746         if(this.getVisibilityEl().hasClass('hidden')){
19747             return true;
19748         }
19749         
19750         if(value.length < 1)  {
19751             if(this.allowBlank){
19752                 return true;
19753             }
19754             return false;
19755         }
19756         
19757         if(value.length < this.minLength){
19758             return false;
19759         }
19760         if(value.length > this.maxLength){
19761             return false;
19762         }
19763         if(this.vtype){
19764             var vt = Roo.form.VTypes;
19765             if(!vt[this.vtype](value, this)){
19766                 return false;
19767             }
19768         }
19769         if(typeof this.validator == "function"){
19770             var msg = this.validator(value);
19771             if(msg !== true){
19772                 return false;
19773             }
19774         }
19775         
19776         if(this.regex && !this.regex.test(value)){
19777             return false;
19778         }
19779         
19780         if(typeof(this.parseDate(value)) == 'undefined'){
19781             return false;
19782         }
19783         
19784         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19785             return false;
19786         }      
19787         
19788         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19789             return false;
19790         } 
19791         
19792         
19793         return true;
19794     },
19795     
19796     reset : function()
19797     {
19798         this.date = this.viewDate = '';
19799         
19800         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19801     }
19802    
19803 });
19804
19805 Roo.apply(Roo.bootstrap.DateField,  {
19806     
19807     head : {
19808         tag: 'thead',
19809         cn: [
19810         {
19811             tag: 'tr',
19812             cn: [
19813             {
19814                 tag: 'th',
19815                 cls: 'prev',
19816                 html: '<i class="fa fa-arrow-left"/>'
19817             },
19818             {
19819                 tag: 'th',
19820                 cls: 'switch',
19821                 colspan: '5'
19822             },
19823             {
19824                 tag: 'th',
19825                 cls: 'next',
19826                 html: '<i class="fa fa-arrow-right"/>'
19827             }
19828
19829             ]
19830         }
19831         ]
19832     },
19833     
19834     content : {
19835         tag: 'tbody',
19836         cn: [
19837         {
19838             tag: 'tr',
19839             cn: [
19840             {
19841                 tag: 'td',
19842                 colspan: '7'
19843             }
19844             ]
19845         }
19846         ]
19847     },
19848     
19849     footer : {
19850         tag: 'tfoot',
19851         cn: [
19852         {
19853             tag: 'tr',
19854             cn: [
19855             {
19856                 tag: 'th',
19857                 colspan: '7',
19858                 cls: 'today'
19859             }
19860                     
19861             ]
19862         }
19863         ]
19864     },
19865     
19866     dates:{
19867         en: {
19868             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19869             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19870             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19871             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19872             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19873             today: "Today"
19874         }
19875     },
19876     
19877     modes: [
19878     {
19879         clsName: 'days',
19880         navFnc: 'Month',
19881         navStep: 1
19882     },
19883     {
19884         clsName: 'months',
19885         navFnc: 'FullYear',
19886         navStep: 1
19887     },
19888     {
19889         clsName: 'years',
19890         navFnc: 'FullYear',
19891         navStep: 10
19892     }]
19893 });
19894
19895 Roo.apply(Roo.bootstrap.DateField,  {
19896   
19897     template : {
19898         tag: 'div',
19899         cls: 'datepicker dropdown-menu roo-dynamic',
19900         cn: [
19901         {
19902             tag: 'div',
19903             cls: 'datepicker-days',
19904             cn: [
19905             {
19906                 tag: 'table',
19907                 cls: 'table-condensed',
19908                 cn:[
19909                 Roo.bootstrap.DateField.head,
19910                 {
19911                     tag: 'tbody'
19912                 },
19913                 Roo.bootstrap.DateField.footer
19914                 ]
19915             }
19916             ]
19917         },
19918         {
19919             tag: 'div',
19920             cls: 'datepicker-months',
19921             cn: [
19922             {
19923                 tag: 'table',
19924                 cls: 'table-condensed',
19925                 cn:[
19926                 Roo.bootstrap.DateField.head,
19927                 Roo.bootstrap.DateField.content,
19928                 Roo.bootstrap.DateField.footer
19929                 ]
19930             }
19931             ]
19932         },
19933         {
19934             tag: 'div',
19935             cls: 'datepicker-years',
19936             cn: [
19937             {
19938                 tag: 'table',
19939                 cls: 'table-condensed',
19940                 cn:[
19941                 Roo.bootstrap.DateField.head,
19942                 Roo.bootstrap.DateField.content,
19943                 Roo.bootstrap.DateField.footer
19944                 ]
19945             }
19946             ]
19947         }
19948         ]
19949     }
19950 });
19951
19952  
19953
19954  /*
19955  * - LGPL
19956  *
19957  * TimeField
19958  * 
19959  */
19960
19961 /**
19962  * @class Roo.bootstrap.TimeField
19963  * @extends Roo.bootstrap.Input
19964  * Bootstrap DateField class
19965  * 
19966  * 
19967  * @constructor
19968  * Create a new TimeField
19969  * @param {Object} config The config object
19970  */
19971
19972 Roo.bootstrap.TimeField = function(config){
19973     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19974     this.addEvents({
19975             /**
19976              * @event show
19977              * Fires when this field show.
19978              * @param {Roo.bootstrap.DateField} thisthis
19979              * @param {Mixed} date The date value
19980              */
19981             show : true,
19982             /**
19983              * @event show
19984              * Fires when this field hide.
19985              * @param {Roo.bootstrap.DateField} this
19986              * @param {Mixed} date The date value
19987              */
19988             hide : true,
19989             /**
19990              * @event select
19991              * Fires when select a date.
19992              * @param {Roo.bootstrap.DateField} this
19993              * @param {Mixed} date The date value
19994              */
19995             select : true
19996         });
19997 };
19998
19999 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
20000     
20001     /**
20002      * @cfg {String} format
20003      * The default time format string which can be overriden for localization support.  The format must be
20004      * valid according to {@link Date#parseDate} (defaults to 'H:i').
20005      */
20006     format : "H:i",
20007        
20008     onRender: function(ct, position)
20009     {
20010         
20011         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20012                 
20013         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20014         
20015         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20016         
20017         this.pop = this.picker().select('>.datepicker-time',true).first();
20018         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20019         
20020         this.picker().on('mousedown', this.onMousedown, this);
20021         this.picker().on('click', this.onClick, this);
20022         
20023         this.picker().addClass('datepicker-dropdown');
20024     
20025         this.fillTime();
20026         this.update();
20027             
20028         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20029         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20030         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20031         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20032         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20033         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20034
20035     },
20036     
20037     fireKey: function(e){
20038         if (!this.picker().isVisible()){
20039             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20040                 this.show();
20041             }
20042             return;
20043         }
20044
20045         e.preventDefault();
20046         
20047         switch(e.keyCode){
20048             case 27: // escape
20049                 this.hide();
20050                 break;
20051             case 37: // left
20052             case 39: // right
20053                 this.onTogglePeriod();
20054                 break;
20055             case 38: // up
20056                 this.onIncrementMinutes();
20057                 break;
20058             case 40: // down
20059                 this.onDecrementMinutes();
20060                 break;
20061             case 13: // enter
20062             case 9: // tab
20063                 this.setTime();
20064                 break;
20065         }
20066     },
20067     
20068     onClick: function(e) {
20069         e.stopPropagation();
20070         e.preventDefault();
20071     },
20072     
20073     picker : function()
20074     {
20075         return this.el.select('.datepicker', true).first();
20076     },
20077     
20078     fillTime: function()
20079     {    
20080         var time = this.pop.select('tbody', true).first();
20081         
20082         time.dom.innerHTML = '';
20083         
20084         time.createChild({
20085             tag: 'tr',
20086             cn: [
20087                 {
20088                     tag: 'td',
20089                     cn: [
20090                         {
20091                             tag: 'a',
20092                             href: '#',
20093                             cls: 'btn',
20094                             cn: [
20095                                 {
20096                                     tag: 'span',
20097                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20098                                 }
20099                             ]
20100                         } 
20101                     ]
20102                 },
20103                 {
20104                     tag: 'td',
20105                     cls: 'separator'
20106                 },
20107                 {
20108                     tag: 'td',
20109                     cn: [
20110                         {
20111                             tag: 'a',
20112                             href: '#',
20113                             cls: 'btn',
20114                             cn: [
20115                                 {
20116                                     tag: 'span',
20117                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20118                                 }
20119                             ]
20120                         }
20121                     ]
20122                 },
20123                 {
20124                     tag: 'td',
20125                     cls: 'separator'
20126                 }
20127             ]
20128         });
20129         
20130         time.createChild({
20131             tag: 'tr',
20132             cn: [
20133                 {
20134                     tag: 'td',
20135                     cn: [
20136                         {
20137                             tag: 'span',
20138                             cls: 'timepicker-hour',
20139                             html: '00'
20140                         }  
20141                     ]
20142                 },
20143                 {
20144                     tag: 'td',
20145                     cls: 'separator',
20146                     html: ':'
20147                 },
20148                 {
20149                     tag: 'td',
20150                     cn: [
20151                         {
20152                             tag: 'span',
20153                             cls: 'timepicker-minute',
20154                             html: '00'
20155                         }  
20156                     ]
20157                 },
20158                 {
20159                     tag: 'td',
20160                     cls: 'separator'
20161                 },
20162                 {
20163                     tag: 'td',
20164                     cn: [
20165                         {
20166                             tag: 'button',
20167                             type: 'button',
20168                             cls: 'btn btn-primary period',
20169                             html: 'AM'
20170                             
20171                         }
20172                     ]
20173                 }
20174             ]
20175         });
20176         
20177         time.createChild({
20178             tag: 'tr',
20179             cn: [
20180                 {
20181                     tag: 'td',
20182                     cn: [
20183                         {
20184                             tag: 'a',
20185                             href: '#',
20186                             cls: 'btn',
20187                             cn: [
20188                                 {
20189                                     tag: 'span',
20190                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20191                                 }
20192                             ]
20193                         }
20194                     ]
20195                 },
20196                 {
20197                     tag: 'td',
20198                     cls: 'separator'
20199                 },
20200                 {
20201                     tag: 'td',
20202                     cn: [
20203                         {
20204                             tag: 'a',
20205                             href: '#',
20206                             cls: 'btn',
20207                             cn: [
20208                                 {
20209                                     tag: 'span',
20210                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20211                                 }
20212                             ]
20213                         }
20214                     ]
20215                 },
20216                 {
20217                     tag: 'td',
20218                     cls: 'separator'
20219                 }
20220             ]
20221         });
20222         
20223     },
20224     
20225     update: function()
20226     {
20227         
20228         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20229         
20230         this.fill();
20231     },
20232     
20233     fill: function() 
20234     {
20235         var hours = this.time.getHours();
20236         var minutes = this.time.getMinutes();
20237         var period = 'AM';
20238         
20239         if(hours > 11){
20240             period = 'PM';
20241         }
20242         
20243         if(hours == 0){
20244             hours = 12;
20245         }
20246         
20247         
20248         if(hours > 12){
20249             hours = hours - 12;
20250         }
20251         
20252         if(hours < 10){
20253             hours = '0' + hours;
20254         }
20255         
20256         if(minutes < 10){
20257             minutes = '0' + minutes;
20258         }
20259         
20260         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20261         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20262         this.pop.select('button', true).first().dom.innerHTML = period;
20263         
20264     },
20265     
20266     place: function()
20267     {   
20268         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20269         
20270         var cls = ['bottom'];
20271         
20272         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20273             cls.pop();
20274             cls.push('top');
20275         }
20276         
20277         cls.push('right');
20278         
20279         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20280             cls.pop();
20281             cls.push('left');
20282         }
20283         
20284         this.picker().addClass(cls.join('-'));
20285         
20286         var _this = this;
20287         
20288         Roo.each(cls, function(c){
20289             if(c == 'bottom'){
20290                 _this.picker().setTop(_this.inputEl().getHeight());
20291                 return;
20292             }
20293             if(c == 'top'){
20294                 _this.picker().setTop(0 - _this.picker().getHeight());
20295                 return;
20296             }
20297             
20298             if(c == 'left'){
20299                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20300                 return;
20301             }
20302             if(c == 'right'){
20303                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20304                 return;
20305             }
20306         });
20307         
20308     },
20309   
20310     onFocus : function()
20311     {
20312         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20313         this.show();
20314     },
20315     
20316     onBlur : function()
20317     {
20318         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20319         this.hide();
20320     },
20321     
20322     show : function()
20323     {
20324         this.picker().show();
20325         this.pop.show();
20326         this.update();
20327         this.place();
20328         
20329         this.fireEvent('show', this, this.date);
20330     },
20331     
20332     hide : function()
20333     {
20334         this.picker().hide();
20335         this.pop.hide();
20336         
20337         this.fireEvent('hide', this, this.date);
20338     },
20339     
20340     setTime : function()
20341     {
20342         this.hide();
20343         this.setValue(this.time.format(this.format));
20344         
20345         this.fireEvent('select', this, this.date);
20346         
20347         
20348     },
20349     
20350     onMousedown: function(e){
20351         e.stopPropagation();
20352         e.preventDefault();
20353     },
20354     
20355     onIncrementHours: function()
20356     {
20357         Roo.log('onIncrementHours');
20358         this.time = this.time.add(Date.HOUR, 1);
20359         this.update();
20360         
20361     },
20362     
20363     onDecrementHours: function()
20364     {
20365         Roo.log('onDecrementHours');
20366         this.time = this.time.add(Date.HOUR, -1);
20367         this.update();
20368     },
20369     
20370     onIncrementMinutes: function()
20371     {
20372         Roo.log('onIncrementMinutes');
20373         this.time = this.time.add(Date.MINUTE, 1);
20374         this.update();
20375     },
20376     
20377     onDecrementMinutes: function()
20378     {
20379         Roo.log('onDecrementMinutes');
20380         this.time = this.time.add(Date.MINUTE, -1);
20381         this.update();
20382     },
20383     
20384     onTogglePeriod: function()
20385     {
20386         Roo.log('onTogglePeriod');
20387         this.time = this.time.add(Date.HOUR, 12);
20388         this.update();
20389     }
20390     
20391    
20392 });
20393
20394 Roo.apply(Roo.bootstrap.TimeField,  {
20395     
20396     content : {
20397         tag: 'tbody',
20398         cn: [
20399             {
20400                 tag: 'tr',
20401                 cn: [
20402                 {
20403                     tag: 'td',
20404                     colspan: '7'
20405                 }
20406                 ]
20407             }
20408         ]
20409     },
20410     
20411     footer : {
20412         tag: 'tfoot',
20413         cn: [
20414             {
20415                 tag: 'tr',
20416                 cn: [
20417                 {
20418                     tag: 'th',
20419                     colspan: '7',
20420                     cls: '',
20421                     cn: [
20422                         {
20423                             tag: 'button',
20424                             cls: 'btn btn-info ok',
20425                             html: 'OK'
20426                         }
20427                     ]
20428                 }
20429
20430                 ]
20431             }
20432         ]
20433     }
20434 });
20435
20436 Roo.apply(Roo.bootstrap.TimeField,  {
20437   
20438     template : {
20439         tag: 'div',
20440         cls: 'datepicker dropdown-menu',
20441         cn: [
20442             {
20443                 tag: 'div',
20444                 cls: 'datepicker-time',
20445                 cn: [
20446                 {
20447                     tag: 'table',
20448                     cls: 'table-condensed',
20449                     cn:[
20450                     Roo.bootstrap.TimeField.content,
20451                     Roo.bootstrap.TimeField.footer
20452                     ]
20453                 }
20454                 ]
20455             }
20456         ]
20457     }
20458 });
20459
20460  
20461
20462  /*
20463  * - LGPL
20464  *
20465  * MonthField
20466  * 
20467  */
20468
20469 /**
20470  * @class Roo.bootstrap.MonthField
20471  * @extends Roo.bootstrap.Input
20472  * Bootstrap MonthField class
20473  * 
20474  * @cfg {String} language default en
20475  * 
20476  * @constructor
20477  * Create a new MonthField
20478  * @param {Object} config The config object
20479  */
20480
20481 Roo.bootstrap.MonthField = function(config){
20482     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20483     
20484     this.addEvents({
20485         /**
20486          * @event show
20487          * Fires when this field show.
20488          * @param {Roo.bootstrap.MonthField} this
20489          * @param {Mixed} date The date value
20490          */
20491         show : true,
20492         /**
20493          * @event show
20494          * Fires when this field hide.
20495          * @param {Roo.bootstrap.MonthField} this
20496          * @param {Mixed} date The date value
20497          */
20498         hide : true,
20499         /**
20500          * @event select
20501          * Fires when select a date.
20502          * @param {Roo.bootstrap.MonthField} this
20503          * @param {String} oldvalue The old value
20504          * @param {String} newvalue The new value
20505          */
20506         select : true
20507     });
20508 };
20509
20510 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20511     
20512     onRender: function(ct, position)
20513     {
20514         
20515         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20516         
20517         this.language = this.language || 'en';
20518         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20519         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20520         
20521         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20522         this.isInline = false;
20523         this.isInput = true;
20524         this.component = this.el.select('.add-on', true).first() || false;
20525         this.component = (this.component && this.component.length === 0) ? false : this.component;
20526         this.hasInput = this.component && this.inputEL().length;
20527         
20528         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20529         
20530         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20531         
20532         this.picker().on('mousedown', this.onMousedown, this);
20533         this.picker().on('click', this.onClick, this);
20534         
20535         this.picker().addClass('datepicker-dropdown');
20536         
20537         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20538             v.setStyle('width', '189px');
20539         });
20540         
20541         this.fillMonths();
20542         
20543         this.update();
20544         
20545         if(this.isInline) {
20546             this.show();
20547         }
20548         
20549     },
20550     
20551     setValue: function(v, suppressEvent)
20552     {   
20553         var o = this.getValue();
20554         
20555         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20556         
20557         this.update();
20558
20559         if(suppressEvent !== true){
20560             this.fireEvent('select', this, o, v);
20561         }
20562         
20563     },
20564     
20565     getValue: function()
20566     {
20567         return this.value;
20568     },
20569     
20570     onClick: function(e) 
20571     {
20572         e.stopPropagation();
20573         e.preventDefault();
20574         
20575         var target = e.getTarget();
20576         
20577         if(target.nodeName.toLowerCase() === 'i'){
20578             target = Roo.get(target).dom.parentNode;
20579         }
20580         
20581         var nodeName = target.nodeName;
20582         var className = target.className;
20583         var html = target.innerHTML;
20584         
20585         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20586             return;
20587         }
20588         
20589         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20590         
20591         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20592         
20593         this.hide();
20594                         
20595     },
20596     
20597     picker : function()
20598     {
20599         return this.pickerEl;
20600     },
20601     
20602     fillMonths: function()
20603     {    
20604         var i = 0;
20605         var months = this.picker().select('>.datepicker-months td', true).first();
20606         
20607         months.dom.innerHTML = '';
20608         
20609         while (i < 12) {
20610             var month = {
20611                 tag: 'span',
20612                 cls: 'month',
20613                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20614             };
20615             
20616             months.createChild(month);
20617         }
20618         
20619     },
20620     
20621     update: function()
20622     {
20623         var _this = this;
20624         
20625         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20626             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20627         }
20628         
20629         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20630             e.removeClass('active');
20631             
20632             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20633                 e.addClass('active');
20634             }
20635         })
20636     },
20637     
20638     place: function()
20639     {
20640         if(this.isInline) {
20641             return;
20642         }
20643         
20644         this.picker().removeClass(['bottom', 'top']);
20645         
20646         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20647             /*
20648              * place to the top of element!
20649              *
20650              */
20651             
20652             this.picker().addClass('top');
20653             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20654             
20655             return;
20656         }
20657         
20658         this.picker().addClass('bottom');
20659         
20660         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20661     },
20662     
20663     onFocus : function()
20664     {
20665         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20666         this.show();
20667     },
20668     
20669     onBlur : function()
20670     {
20671         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20672         
20673         var d = this.inputEl().getValue();
20674         
20675         this.setValue(d);
20676                 
20677         this.hide();
20678     },
20679     
20680     show : function()
20681     {
20682         this.picker().show();
20683         this.picker().select('>.datepicker-months', true).first().show();
20684         this.update();
20685         this.place();
20686         
20687         this.fireEvent('show', this, this.date);
20688     },
20689     
20690     hide : function()
20691     {
20692         if(this.isInline) {
20693             return;
20694         }
20695         this.picker().hide();
20696         this.fireEvent('hide', this, this.date);
20697         
20698     },
20699     
20700     onMousedown: function(e)
20701     {
20702         e.stopPropagation();
20703         e.preventDefault();
20704     },
20705     
20706     keyup: function(e)
20707     {
20708         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20709         this.update();
20710     },
20711
20712     fireKey: function(e)
20713     {
20714         if (!this.picker().isVisible()){
20715             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20716                 this.show();
20717             }
20718             return;
20719         }
20720         
20721         var dir;
20722         
20723         switch(e.keyCode){
20724             case 27: // escape
20725                 this.hide();
20726                 e.preventDefault();
20727                 break;
20728             case 37: // left
20729             case 39: // right
20730                 dir = e.keyCode == 37 ? -1 : 1;
20731                 
20732                 this.vIndex = this.vIndex + dir;
20733                 
20734                 if(this.vIndex < 0){
20735                     this.vIndex = 0;
20736                 }
20737                 
20738                 if(this.vIndex > 11){
20739                     this.vIndex = 11;
20740                 }
20741                 
20742                 if(isNaN(this.vIndex)){
20743                     this.vIndex = 0;
20744                 }
20745                 
20746                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20747                 
20748                 break;
20749             case 38: // up
20750             case 40: // down
20751                 
20752                 dir = e.keyCode == 38 ? -1 : 1;
20753                 
20754                 this.vIndex = this.vIndex + dir * 4;
20755                 
20756                 if(this.vIndex < 0){
20757                     this.vIndex = 0;
20758                 }
20759                 
20760                 if(this.vIndex > 11){
20761                     this.vIndex = 11;
20762                 }
20763                 
20764                 if(isNaN(this.vIndex)){
20765                     this.vIndex = 0;
20766                 }
20767                 
20768                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20769                 break;
20770                 
20771             case 13: // enter
20772                 
20773                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20774                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20775                 }
20776                 
20777                 this.hide();
20778                 e.preventDefault();
20779                 break;
20780             case 9: // tab
20781                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20782                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20783                 }
20784                 this.hide();
20785                 break;
20786             case 16: // shift
20787             case 17: // ctrl
20788             case 18: // alt
20789                 break;
20790             default :
20791                 this.hide();
20792                 
20793         }
20794     },
20795     
20796     remove: function() 
20797     {
20798         this.picker().remove();
20799     }
20800    
20801 });
20802
20803 Roo.apply(Roo.bootstrap.MonthField,  {
20804     
20805     content : {
20806         tag: 'tbody',
20807         cn: [
20808         {
20809             tag: 'tr',
20810             cn: [
20811             {
20812                 tag: 'td',
20813                 colspan: '7'
20814             }
20815             ]
20816         }
20817         ]
20818     },
20819     
20820     dates:{
20821         en: {
20822             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20823             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20824         }
20825     }
20826 });
20827
20828 Roo.apply(Roo.bootstrap.MonthField,  {
20829   
20830     template : {
20831         tag: 'div',
20832         cls: 'datepicker dropdown-menu roo-dynamic',
20833         cn: [
20834             {
20835                 tag: 'div',
20836                 cls: 'datepicker-months',
20837                 cn: [
20838                 {
20839                     tag: 'table',
20840                     cls: 'table-condensed',
20841                     cn:[
20842                         Roo.bootstrap.DateField.content
20843                     ]
20844                 }
20845                 ]
20846             }
20847         ]
20848     }
20849 });
20850
20851  
20852
20853  
20854  /*
20855  * - LGPL
20856  *
20857  * CheckBox
20858  * 
20859  */
20860
20861 /**
20862  * @class Roo.bootstrap.CheckBox
20863  * @extends Roo.bootstrap.Input
20864  * Bootstrap CheckBox class
20865  * 
20866  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20867  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20868  * @cfg {String} boxLabel The text that appears beside the checkbox
20869  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20870  * @cfg {Boolean} checked initnal the element
20871  * @cfg {Boolean} inline inline the element (default false)
20872  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20873  * @cfg {String} tooltip label tooltip
20874  * 
20875  * @constructor
20876  * Create a new CheckBox
20877  * @param {Object} config The config object
20878  */
20879
20880 Roo.bootstrap.CheckBox = function(config){
20881     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20882    
20883     this.addEvents({
20884         /**
20885         * @event check
20886         * Fires when the element is checked or unchecked.
20887         * @param {Roo.bootstrap.CheckBox} this This input
20888         * @param {Boolean} checked The new checked value
20889         */
20890        check : true,
20891        /**
20892         * @event click
20893         * Fires when the element is click.
20894         * @param {Roo.bootstrap.CheckBox} this This input
20895         */
20896        click : true
20897     });
20898     
20899 };
20900
20901 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20902   
20903     inputType: 'checkbox',
20904     inputValue: 1,
20905     valueOff: 0,
20906     boxLabel: false,
20907     checked: false,
20908     weight : false,
20909     inline: false,
20910     tooltip : '',
20911     
20912     // checkbox success does not make any sense really.. 
20913     invalidClass : "",
20914     validClass : "",
20915     
20916     
20917     getAutoCreate : function()
20918     {
20919         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20920         
20921         var id = Roo.id();
20922         
20923         var cfg = {};
20924         
20925         cfg.cls = 'form-group ' + this.inputType; //input-group
20926         
20927         if(this.inline){
20928             cfg.cls += ' ' + this.inputType + '-inline';
20929         }
20930         
20931         var input =  {
20932             tag: 'input',
20933             id : id,
20934             type : this.inputType,
20935             value : this.inputValue,
20936             cls : 'roo-' + this.inputType, //'form-box',
20937             placeholder : this.placeholder || ''
20938             
20939         };
20940         
20941         if(this.inputType != 'radio'){
20942             var hidden =  {
20943                 tag: 'input',
20944                 type : 'hidden',
20945                 cls : 'roo-hidden-value',
20946                 value : this.checked ? this.inputValue : this.valueOff
20947             };
20948         }
20949         
20950             
20951         if (this.weight) { // Validity check?
20952             cfg.cls += " " + this.inputType + "-" + this.weight;
20953         }
20954         
20955         if (this.disabled) {
20956             input.disabled=true;
20957         }
20958         
20959         if(this.checked){
20960             input.checked = this.checked;
20961         }
20962         
20963         if (this.name) {
20964             
20965             input.name = this.name;
20966             
20967             if(this.inputType != 'radio'){
20968                 hidden.name = this.name;
20969                 input.name = '_hidden_' + this.name;
20970             }
20971         }
20972         
20973         if (this.size) {
20974             input.cls += ' input-' + this.size;
20975         }
20976         
20977         var settings=this;
20978         
20979         ['xs','sm','md','lg'].map(function(size){
20980             if (settings[size]) {
20981                 cfg.cls += ' col-' + size + '-' + settings[size];
20982             }
20983         });
20984         
20985         var inputblock = input;
20986          
20987         if (this.before || this.after) {
20988             
20989             inputblock = {
20990                 cls : 'input-group',
20991                 cn :  [] 
20992             };
20993             
20994             if (this.before) {
20995                 inputblock.cn.push({
20996                     tag :'span',
20997                     cls : 'input-group-addon',
20998                     html : this.before
20999                 });
21000             }
21001             
21002             inputblock.cn.push(input);
21003             
21004             if(this.inputType != 'radio'){
21005                 inputblock.cn.push(hidden);
21006             }
21007             
21008             if (this.after) {
21009                 inputblock.cn.push({
21010                     tag :'span',
21011                     cls : 'input-group-addon',
21012                     html : this.after
21013                 });
21014             }
21015             
21016         }
21017         var boxLabelCfg = false;
21018         
21019         if(this.boxLabel){
21020            
21021             boxLabelCfg = {
21022                 tag: 'label',
21023                 //'for': id, // box label is handled by onclick - so no for...
21024                 cls: 'box-label',
21025                 html: this.boxLabel
21026             };
21027             if(this.tooltip){
21028                 boxLabelCfg.tooltip = this.tooltip;
21029             }
21030              
21031         }
21032         
21033         
21034         if (align ==='left' && this.fieldLabel.length) {
21035 //                Roo.log("left and has label");
21036             cfg.cn = [
21037                 {
21038                     tag: 'label',
21039                     'for' :  id,
21040                     cls : 'control-label',
21041                     html : this.fieldLabel
21042                 },
21043                 {
21044                     cls : "", 
21045                     cn: [
21046                         inputblock
21047                     ]
21048                 }
21049             ];
21050             
21051             if (boxLabelCfg) {
21052                 cfg.cn[1].cn.push(boxLabelCfg);
21053             }
21054             
21055             if(this.labelWidth > 12){
21056                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21057             }
21058             
21059             if(this.labelWidth < 13 && this.labelmd == 0){
21060                 this.labelmd = this.labelWidth;
21061             }
21062             
21063             if(this.labellg > 0){
21064                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21065                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21066             }
21067             
21068             if(this.labelmd > 0){
21069                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21070                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21071             }
21072             
21073             if(this.labelsm > 0){
21074                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21075                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21076             }
21077             
21078             if(this.labelxs > 0){
21079                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21080                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21081             }
21082             
21083         } else if ( this.fieldLabel.length) {
21084 //                Roo.log(" label");
21085                 cfg.cn = [
21086                    
21087                     {
21088                         tag: this.boxLabel ? 'span' : 'label',
21089                         'for': id,
21090                         cls: 'control-label box-input-label',
21091                         //cls : 'input-group-addon',
21092                         html : this.fieldLabel
21093                     },
21094                     
21095                     inputblock
21096                     
21097                 ];
21098                 if (boxLabelCfg) {
21099                     cfg.cn.push(boxLabelCfg);
21100                 }
21101
21102         } else {
21103             
21104 //                Roo.log(" no label && no align");
21105                 cfg.cn = [  inputblock ] ;
21106                 if (boxLabelCfg) {
21107                     cfg.cn.push(boxLabelCfg);
21108                 }
21109
21110                 
21111         }
21112         
21113        
21114         
21115         if(this.inputType != 'radio'){
21116             cfg.cn.push(hidden);
21117         }
21118         
21119         return cfg;
21120         
21121     },
21122     
21123     /**
21124      * return the real input element.
21125      */
21126     inputEl: function ()
21127     {
21128         return this.el.select('input.roo-' + this.inputType,true).first();
21129     },
21130     hiddenEl: function ()
21131     {
21132         return this.el.select('input.roo-hidden-value',true).first();
21133     },
21134     
21135     labelEl: function()
21136     {
21137         return this.el.select('label.control-label',true).first();
21138     },
21139     /* depricated... */
21140     
21141     label: function()
21142     {
21143         return this.labelEl();
21144     },
21145     
21146     boxLabelEl: function()
21147     {
21148         return this.el.select('label.box-label',true).first();
21149     },
21150     
21151     initEvents : function()
21152     {
21153 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21154         
21155         this.inputEl().on('click', this.onClick,  this);
21156         
21157         if (this.boxLabel) { 
21158             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21159         }
21160         
21161         this.startValue = this.getValue();
21162         
21163         if(this.groupId){
21164             Roo.bootstrap.CheckBox.register(this);
21165         }
21166     },
21167     
21168     onClick : function(e)
21169     {   
21170         if(this.fireEvent('click', this, e) !== false){
21171             this.setChecked(!this.checked);
21172         }
21173         
21174     },
21175     
21176     setChecked : function(state,suppressEvent)
21177     {
21178         this.startValue = this.getValue();
21179
21180         if(this.inputType == 'radio'){
21181             
21182             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21183                 e.dom.checked = false;
21184             });
21185             
21186             this.inputEl().dom.checked = true;
21187             
21188             this.inputEl().dom.value = this.inputValue;
21189             
21190             if(suppressEvent !== true){
21191                 this.fireEvent('check', this, true);
21192             }
21193             
21194             this.validate();
21195             
21196             return;
21197         }
21198         
21199         this.checked = state;
21200         
21201         this.inputEl().dom.checked = state;
21202         
21203         
21204         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21205         
21206         if(suppressEvent !== true){
21207             this.fireEvent('check', this, state);
21208         }
21209         
21210         this.validate();
21211     },
21212     
21213     getValue : function()
21214     {
21215         if(this.inputType == 'radio'){
21216             return this.getGroupValue();
21217         }
21218         
21219         return this.hiddenEl().dom.value;
21220         
21221     },
21222     
21223     getGroupValue : function()
21224     {
21225         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21226             return '';
21227         }
21228         
21229         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21230     },
21231     
21232     setValue : function(v,suppressEvent)
21233     {
21234         if(this.inputType == 'radio'){
21235             this.setGroupValue(v, suppressEvent);
21236             return;
21237         }
21238         
21239         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21240         
21241         this.validate();
21242     },
21243     
21244     setGroupValue : function(v, suppressEvent)
21245     {
21246         this.startValue = this.getValue();
21247         
21248         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21249             e.dom.checked = false;
21250             
21251             if(e.dom.value == v){
21252                 e.dom.checked = true;
21253             }
21254         });
21255         
21256         if(suppressEvent !== true){
21257             this.fireEvent('check', this, true);
21258         }
21259
21260         this.validate();
21261         
21262         return;
21263     },
21264     
21265     validate : function()
21266     {
21267         if(this.getVisibilityEl().hasClass('hidden')){
21268             return true;
21269         }
21270         
21271         if(
21272                 this.disabled || 
21273                 (this.inputType == 'radio' && this.validateRadio()) ||
21274                 (this.inputType == 'checkbox' && this.validateCheckbox())
21275         ){
21276             this.markValid();
21277             return true;
21278         }
21279         
21280         this.markInvalid();
21281         return false;
21282     },
21283     
21284     validateRadio : function()
21285     {
21286         if(this.getVisibilityEl().hasClass('hidden')){
21287             return true;
21288         }
21289         
21290         if(this.allowBlank){
21291             return true;
21292         }
21293         
21294         var valid = false;
21295         
21296         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21297             if(!e.dom.checked){
21298                 return;
21299             }
21300             
21301             valid = true;
21302             
21303             return false;
21304         });
21305         
21306         return valid;
21307     },
21308     
21309     validateCheckbox : function()
21310     {
21311         if(!this.groupId){
21312             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21313             //return (this.getValue() == this.inputValue) ? true : false;
21314         }
21315         
21316         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21317         
21318         if(!group){
21319             return false;
21320         }
21321         
21322         var r = false;
21323         
21324         for(var i in group){
21325             if(group[i].el.isVisible(true)){
21326                 r = false;
21327                 break;
21328             }
21329             
21330             r = true;
21331         }
21332         
21333         for(var i in group){
21334             if(r){
21335                 break;
21336             }
21337             
21338             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21339         }
21340         
21341         return r;
21342     },
21343     
21344     /**
21345      * Mark this field as valid
21346      */
21347     markValid : function()
21348     {
21349         var _this = this;
21350         
21351         this.fireEvent('valid', this);
21352         
21353         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21354         
21355         if(this.groupId){
21356             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21357         }
21358         
21359         if(label){
21360             label.markValid();
21361         }
21362
21363         if(this.inputType == 'radio'){
21364             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21365                 var fg = e.findParent('.form-group', false, true);
21366                 if (Roo.bootstrap.version == 3) {
21367                     fg.removeClass([_this.invalidClass, _this.validClass]);
21368                     fg.addClass(_this.validClass);
21369                 } else {
21370                     fg.removeClass(['is-valid', 'is-invalid']);
21371                     fg.addClass('is-valid');
21372                 }
21373             });
21374             
21375             return;
21376         }
21377
21378         if(!this.groupId){
21379             var fg = this.el.findParent('.form-group', false, true);
21380             if (Roo.bootstrap.version == 3) {
21381                 fg.removeClass([this.invalidClass, this.validClass]);
21382                 fg.addClass(this.validClass);
21383             } else {
21384                 fg.removeClass(['is-valid', 'is-invalid']);
21385                 fg.addClass('is-valid');
21386             }
21387             return;
21388         }
21389         
21390         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21391         
21392         if(!group){
21393             return;
21394         }
21395         
21396         for(var i in group){
21397             var fg = group[i].el.findParent('.form-group', false, true);
21398             if (Roo.bootstrap.version == 3) {
21399                 fg.removeClass([this.invalidClass, this.validClass]);
21400                 fg.addClass(this.validClass);
21401             } else {
21402                 fg.removeClass(['is-valid', 'is-invalid']);
21403                 fg.addClass('is-valid');
21404             }
21405         }
21406     },
21407     
21408      /**
21409      * Mark this field as invalid
21410      * @param {String} msg The validation message
21411      */
21412     markInvalid : function(msg)
21413     {
21414         if(this.allowBlank){
21415             return;
21416         }
21417         
21418         var _this = this;
21419         
21420         this.fireEvent('invalid', this, msg);
21421         
21422         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21423         
21424         if(this.groupId){
21425             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21426         }
21427         
21428         if(label){
21429             label.markInvalid();
21430         }
21431             
21432         if(this.inputType == 'radio'){
21433             
21434             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21435                 var fg = e.findParent('.form-group', false, true);
21436                 if (Roo.bootstrap.version == 3) {
21437                     fg.removeClass([_this.invalidClass, _this.validClass]);
21438                     fg.addClass(_this.invalidClass);
21439                 } else {
21440                     fg.removeClass(['is-invalid', 'is-valid']);
21441                     fg.addClass('is-invalid');
21442                 }
21443             });
21444             
21445             return;
21446         }
21447         
21448         if(!this.groupId){
21449             var fg = this.el.findParent('.form-group', false, true);
21450             if (Roo.bootstrap.version == 3) {
21451                 fg.removeClass([_this.invalidClass, _this.validClass]);
21452                 fg.addClass(_this.invalidClass);
21453             } else {
21454                 fg.removeClass(['is-invalid', 'is-valid']);
21455                 fg.addClass('is-invalid');
21456             }
21457             return;
21458         }
21459         
21460         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21461         
21462         if(!group){
21463             return;
21464         }
21465         
21466         for(var i in group){
21467             var fg = group[i].el.findParent('.form-group', false, true);
21468             if (Roo.bootstrap.version == 3) {
21469                 fg.removeClass([_this.invalidClass, _this.validClass]);
21470                 fg.addClass(_this.invalidClass);
21471             } else {
21472                 fg.removeClass(['is-invalid', 'is-valid']);
21473                 fg.addClass('is-invalid');
21474             }
21475         }
21476         
21477     },
21478     
21479     clearInvalid : function()
21480     {
21481         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21482         
21483         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21484         
21485         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21486         
21487         if (label && label.iconEl) {
21488             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21489             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21490         }
21491     },
21492     
21493     disable : function()
21494     {
21495         if(this.inputType != 'radio'){
21496             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21497             return;
21498         }
21499         
21500         var _this = this;
21501         
21502         if(this.rendered){
21503             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21504                 _this.getActionEl().addClass(this.disabledClass);
21505                 e.dom.disabled = true;
21506             });
21507         }
21508         
21509         this.disabled = true;
21510         this.fireEvent("disable", this);
21511         return this;
21512     },
21513
21514     enable : function()
21515     {
21516         if(this.inputType != 'radio'){
21517             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21518             return;
21519         }
21520         
21521         var _this = this;
21522         
21523         if(this.rendered){
21524             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21525                 _this.getActionEl().removeClass(this.disabledClass);
21526                 e.dom.disabled = false;
21527             });
21528         }
21529         
21530         this.disabled = false;
21531         this.fireEvent("enable", this);
21532         return this;
21533     },
21534     
21535     setBoxLabel : function(v)
21536     {
21537         this.boxLabel = v;
21538         
21539         if(this.rendered){
21540             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21541         }
21542     }
21543
21544 });
21545
21546 Roo.apply(Roo.bootstrap.CheckBox, {
21547     
21548     groups: {},
21549     
21550      /**
21551     * register a CheckBox Group
21552     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21553     */
21554     register : function(checkbox)
21555     {
21556         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21557             this.groups[checkbox.groupId] = {};
21558         }
21559         
21560         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21561             return;
21562         }
21563         
21564         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21565         
21566     },
21567     /**
21568     * fetch a CheckBox Group based on the group ID
21569     * @param {string} the group ID
21570     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21571     */
21572     get: function(groupId) {
21573         if (typeof(this.groups[groupId]) == 'undefined') {
21574             return false;
21575         }
21576         
21577         return this.groups[groupId] ;
21578     }
21579     
21580     
21581 });
21582 /*
21583  * - LGPL
21584  *
21585  * RadioItem
21586  * 
21587  */
21588
21589 /**
21590  * @class Roo.bootstrap.Radio
21591  * @extends Roo.bootstrap.Component
21592  * Bootstrap Radio class
21593  * @cfg {String} boxLabel - the label associated
21594  * @cfg {String} value - the value of radio
21595  * 
21596  * @constructor
21597  * Create a new Radio
21598  * @param {Object} config The config object
21599  */
21600 Roo.bootstrap.Radio = function(config){
21601     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21602     
21603 };
21604
21605 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21606     
21607     boxLabel : '',
21608     
21609     value : '',
21610     
21611     getAutoCreate : function()
21612     {
21613         var cfg = {
21614             tag : 'div',
21615             cls : 'form-group radio',
21616             cn : [
21617                 {
21618                     tag : 'label',
21619                     cls : 'box-label',
21620                     html : this.boxLabel
21621                 }
21622             ]
21623         };
21624         
21625         return cfg;
21626     },
21627     
21628     initEvents : function() 
21629     {
21630         this.parent().register(this);
21631         
21632         this.el.on('click', this.onClick, this);
21633         
21634     },
21635     
21636     onClick : function(e)
21637     {
21638         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21639             this.setChecked(true);
21640         }
21641     },
21642     
21643     setChecked : function(state, suppressEvent)
21644     {
21645         this.parent().setValue(this.value, suppressEvent);
21646         
21647     },
21648     
21649     setBoxLabel : function(v)
21650     {
21651         this.boxLabel = v;
21652         
21653         if(this.rendered){
21654             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21655         }
21656     }
21657     
21658 });
21659  
21660
21661  /*
21662  * - LGPL
21663  *
21664  * Input
21665  * 
21666  */
21667
21668 /**
21669  * @class Roo.bootstrap.SecurePass
21670  * @extends Roo.bootstrap.Input
21671  * Bootstrap SecurePass class
21672  *
21673  * 
21674  * @constructor
21675  * Create a new SecurePass
21676  * @param {Object} config The config object
21677  */
21678  
21679 Roo.bootstrap.SecurePass = function (config) {
21680     // these go here, so the translation tool can replace them..
21681     this.errors = {
21682         PwdEmpty: "Please type a password, and then retype it to confirm.",
21683         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21684         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21685         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21686         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21687         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21688         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21689         TooWeak: "Your password is Too Weak."
21690     },
21691     this.meterLabel = "Password strength:";
21692     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21693     this.meterClass = [
21694         "roo-password-meter-tooweak", 
21695         "roo-password-meter-weak", 
21696         "roo-password-meter-medium", 
21697         "roo-password-meter-strong", 
21698         "roo-password-meter-grey"
21699     ];
21700     
21701     this.errors = {};
21702     
21703     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21704 }
21705
21706 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21707     /**
21708      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21709      * {
21710      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21711      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21712      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21713      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21714      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21715      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21716      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21717      * })
21718      */
21719     // private
21720     
21721     meterWidth: 300,
21722     errorMsg :'',    
21723     errors: false,
21724     imageRoot: '/',
21725     /**
21726      * @cfg {String/Object} Label for the strength meter (defaults to
21727      * 'Password strength:')
21728      */
21729     // private
21730     meterLabel: '',
21731     /**
21732      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21733      * ['Weak', 'Medium', 'Strong'])
21734      */
21735     // private    
21736     pwdStrengths: false,    
21737     // private
21738     strength: 0,
21739     // private
21740     _lastPwd: null,
21741     // private
21742     kCapitalLetter: 0,
21743     kSmallLetter: 1,
21744     kDigit: 2,
21745     kPunctuation: 3,
21746     
21747     insecure: false,
21748     // private
21749     initEvents: function ()
21750     {
21751         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21752
21753         if (this.el.is('input[type=password]') && Roo.isSafari) {
21754             this.el.on('keydown', this.SafariOnKeyDown, this);
21755         }
21756
21757         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21758     },
21759     // private
21760     onRender: function (ct, position)
21761     {
21762         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21763         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21764         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21765
21766         this.trigger.createChild({
21767                    cn: [
21768                     {
21769                     //id: 'PwdMeter',
21770                     tag: 'div',
21771                     cls: 'roo-password-meter-grey col-xs-12',
21772                     style: {
21773                         //width: 0,
21774                         //width: this.meterWidth + 'px'                                                
21775                         }
21776                     },
21777                     {                            
21778                          cls: 'roo-password-meter-text'                          
21779                     }
21780                 ]            
21781         });
21782
21783          
21784         if (this.hideTrigger) {
21785             this.trigger.setDisplayed(false);
21786         }
21787         this.setSize(this.width || '', this.height || '');
21788     },
21789     // private
21790     onDestroy: function ()
21791     {
21792         if (this.trigger) {
21793             this.trigger.removeAllListeners();
21794             this.trigger.remove();
21795         }
21796         if (this.wrap) {
21797             this.wrap.remove();
21798         }
21799         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21800     },
21801     // private
21802     checkStrength: function ()
21803     {
21804         var pwd = this.inputEl().getValue();
21805         if (pwd == this._lastPwd) {
21806             return;
21807         }
21808
21809         var strength;
21810         if (this.ClientSideStrongPassword(pwd)) {
21811             strength = 3;
21812         } else if (this.ClientSideMediumPassword(pwd)) {
21813             strength = 2;
21814         } else if (this.ClientSideWeakPassword(pwd)) {
21815             strength = 1;
21816         } else {
21817             strength = 0;
21818         }
21819         
21820         Roo.log('strength1: ' + strength);
21821         
21822         //var pm = this.trigger.child('div/div/div').dom;
21823         var pm = this.trigger.child('div/div');
21824         pm.removeClass(this.meterClass);
21825         pm.addClass(this.meterClass[strength]);
21826                 
21827         
21828         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21829                 
21830         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21831         
21832         this._lastPwd = pwd;
21833     },
21834     reset: function ()
21835     {
21836         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21837         
21838         this._lastPwd = '';
21839         
21840         var pm = this.trigger.child('div/div');
21841         pm.removeClass(this.meterClass);
21842         pm.addClass('roo-password-meter-grey');        
21843         
21844         
21845         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21846         
21847         pt.innerHTML = '';
21848         this.inputEl().dom.type='password';
21849     },
21850     // private
21851     validateValue: function (value)
21852     {
21853         
21854         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21855             return false;
21856         }
21857         if (value.length == 0) {
21858             if (this.allowBlank) {
21859                 this.clearInvalid();
21860                 return true;
21861             }
21862
21863             this.markInvalid(this.errors.PwdEmpty);
21864             this.errorMsg = this.errors.PwdEmpty;
21865             return false;
21866         }
21867         
21868         if(this.insecure){
21869             return true;
21870         }
21871         
21872         if ('[\x21-\x7e]*'.match(value)) {
21873             this.markInvalid(this.errors.PwdBadChar);
21874             this.errorMsg = this.errors.PwdBadChar;
21875             return false;
21876         }
21877         if (value.length < 6) {
21878             this.markInvalid(this.errors.PwdShort);
21879             this.errorMsg = this.errors.PwdShort;
21880             return false;
21881         }
21882         if (value.length > 16) {
21883             this.markInvalid(this.errors.PwdLong);
21884             this.errorMsg = this.errors.PwdLong;
21885             return false;
21886         }
21887         var strength;
21888         if (this.ClientSideStrongPassword(value)) {
21889             strength = 3;
21890         } else if (this.ClientSideMediumPassword(value)) {
21891             strength = 2;
21892         } else if (this.ClientSideWeakPassword(value)) {
21893             strength = 1;
21894         } else {
21895             strength = 0;
21896         }
21897
21898         
21899         if (strength < 2) {
21900             //this.markInvalid(this.errors.TooWeak);
21901             this.errorMsg = this.errors.TooWeak;
21902             //return false;
21903         }
21904         
21905         
21906         console.log('strength2: ' + strength);
21907         
21908         //var pm = this.trigger.child('div/div/div').dom;
21909         
21910         var pm = this.trigger.child('div/div');
21911         pm.removeClass(this.meterClass);
21912         pm.addClass(this.meterClass[strength]);
21913                 
21914         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21915                 
21916         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21917         
21918         this.errorMsg = ''; 
21919         return true;
21920     },
21921     // private
21922     CharacterSetChecks: function (type)
21923     {
21924         this.type = type;
21925         this.fResult = false;
21926     },
21927     // private
21928     isctype: function (character, type)
21929     {
21930         switch (type) {  
21931             case this.kCapitalLetter:
21932                 if (character >= 'A' && character <= 'Z') {
21933                     return true;
21934                 }
21935                 break;
21936             
21937             case this.kSmallLetter:
21938                 if (character >= 'a' && character <= 'z') {
21939                     return true;
21940                 }
21941                 break;
21942             
21943             case this.kDigit:
21944                 if (character >= '0' && character <= '9') {
21945                     return true;
21946                 }
21947                 break;
21948             
21949             case this.kPunctuation:
21950                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21951                     return true;
21952                 }
21953                 break;
21954             
21955             default:
21956                 return false;
21957         }
21958
21959     },
21960     // private
21961     IsLongEnough: function (pwd, size)
21962     {
21963         return !(pwd == null || isNaN(size) || pwd.length < size);
21964     },
21965     // private
21966     SpansEnoughCharacterSets: function (word, nb)
21967     {
21968         if (!this.IsLongEnough(word, nb))
21969         {
21970             return false;
21971         }
21972
21973         var characterSetChecks = new Array(
21974             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21975             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21976         );
21977         
21978         for (var index = 0; index < word.length; ++index) {
21979             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21980                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21981                     characterSetChecks[nCharSet].fResult = true;
21982                     break;
21983                 }
21984             }
21985         }
21986
21987         var nCharSets = 0;
21988         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21989             if (characterSetChecks[nCharSet].fResult) {
21990                 ++nCharSets;
21991             }
21992         }
21993
21994         if (nCharSets < nb) {
21995             return false;
21996         }
21997         return true;
21998     },
21999     // private
22000     ClientSideStrongPassword: function (pwd)
22001     {
22002         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
22003     },
22004     // private
22005     ClientSideMediumPassword: function (pwd)
22006     {
22007         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22008     },
22009     // private
22010     ClientSideWeakPassword: function (pwd)
22011     {
22012         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22013     }
22014           
22015 })//<script type="text/javascript">
22016
22017 /*
22018  * Based  Ext JS Library 1.1.1
22019  * Copyright(c) 2006-2007, Ext JS, LLC.
22020  * LGPL
22021  *
22022  */
22023  
22024 /**
22025  * @class Roo.HtmlEditorCore
22026  * @extends Roo.Component
22027  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22028  *
22029  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22030  */
22031
22032 Roo.HtmlEditorCore = function(config){
22033     
22034     
22035     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22036     
22037     
22038     this.addEvents({
22039         /**
22040          * @event initialize
22041          * Fires when the editor is fully initialized (including the iframe)
22042          * @param {Roo.HtmlEditorCore} this
22043          */
22044         initialize: true,
22045         /**
22046          * @event activate
22047          * Fires when the editor is first receives the focus. Any insertion must wait
22048          * until after this event.
22049          * @param {Roo.HtmlEditorCore} this
22050          */
22051         activate: true,
22052          /**
22053          * @event beforesync
22054          * Fires before the textarea is updated with content from the editor iframe. Return false
22055          * to cancel the sync.
22056          * @param {Roo.HtmlEditorCore} this
22057          * @param {String} html
22058          */
22059         beforesync: true,
22060          /**
22061          * @event beforepush
22062          * Fires before the iframe editor is updated with content from the textarea. Return false
22063          * to cancel the push.
22064          * @param {Roo.HtmlEditorCore} this
22065          * @param {String} html
22066          */
22067         beforepush: true,
22068          /**
22069          * @event sync
22070          * Fires when the textarea is updated with content from the editor iframe.
22071          * @param {Roo.HtmlEditorCore} this
22072          * @param {String} html
22073          */
22074         sync: true,
22075          /**
22076          * @event push
22077          * Fires when the iframe editor is updated with content from the textarea.
22078          * @param {Roo.HtmlEditorCore} this
22079          * @param {String} html
22080          */
22081         push: true,
22082         
22083         /**
22084          * @event editorevent
22085          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22086          * @param {Roo.HtmlEditorCore} this
22087          */
22088         editorevent: true
22089         
22090     });
22091     
22092     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22093     
22094     // defaults : white / black...
22095     this.applyBlacklists();
22096     
22097     
22098     
22099 };
22100
22101
22102 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22103
22104
22105      /**
22106      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22107      */
22108     
22109     owner : false,
22110     
22111      /**
22112      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22113      *                        Roo.resizable.
22114      */
22115     resizable : false,
22116      /**
22117      * @cfg {Number} height (in pixels)
22118      */   
22119     height: 300,
22120    /**
22121      * @cfg {Number} width (in pixels)
22122      */   
22123     width: 500,
22124     
22125     /**
22126      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22127      * 
22128      */
22129     stylesheets: false,
22130     
22131     // id of frame..
22132     frameId: false,
22133     
22134     // private properties
22135     validationEvent : false,
22136     deferHeight: true,
22137     initialized : false,
22138     activated : false,
22139     sourceEditMode : false,
22140     onFocus : Roo.emptyFn,
22141     iframePad:3,
22142     hideMode:'offsets',
22143     
22144     clearUp: true,
22145     
22146     // blacklist + whitelisted elements..
22147     black: false,
22148     white: false,
22149      
22150     bodyCls : '',
22151
22152     /**
22153      * Protected method that will not generally be called directly. It
22154      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22155      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22156      */
22157     getDocMarkup : function(){
22158         // body styles..
22159         var st = '';
22160         
22161         // inherit styels from page...?? 
22162         if (this.stylesheets === false) {
22163             
22164             Roo.get(document.head).select('style').each(function(node) {
22165                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22166             });
22167             
22168             Roo.get(document.head).select('link').each(function(node) { 
22169                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22170             });
22171             
22172         } else if (!this.stylesheets.length) {
22173                 // simple..
22174                 st = '<style type="text/css">' +
22175                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22176                    '</style>';
22177         } else { 
22178             st = '<style type="text/css">' +
22179                     this.stylesheets +
22180                 '</style>';
22181         }
22182         
22183         st +=  '<style type="text/css">' +
22184             'IMG { cursor: pointer } ' +
22185         '</style>';
22186
22187         var cls = 'roo-htmleditor-body';
22188         
22189         if(this.bodyCls.length){
22190             cls += ' ' + this.bodyCls;
22191         }
22192         
22193         return '<html><head>' + st  +
22194             //<style type="text/css">' +
22195             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22196             //'</style>' +
22197             ' </head><body class="' +  cls + '"></body></html>';
22198     },
22199
22200     // private
22201     onRender : function(ct, position)
22202     {
22203         var _t = this;
22204         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22205         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22206         
22207         
22208         this.el.dom.style.border = '0 none';
22209         this.el.dom.setAttribute('tabIndex', -1);
22210         this.el.addClass('x-hidden hide');
22211         
22212         
22213         
22214         if(Roo.isIE){ // fix IE 1px bogus margin
22215             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22216         }
22217        
22218         
22219         this.frameId = Roo.id();
22220         
22221          
22222         
22223         var iframe = this.owner.wrap.createChild({
22224             tag: 'iframe',
22225             cls: 'form-control', // bootstrap..
22226             id: this.frameId,
22227             name: this.frameId,
22228             frameBorder : 'no',
22229             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22230         }, this.el
22231         );
22232         
22233         
22234         this.iframe = iframe.dom;
22235
22236          this.assignDocWin();
22237         
22238         this.doc.designMode = 'on';
22239        
22240         this.doc.open();
22241         this.doc.write(this.getDocMarkup());
22242         this.doc.close();
22243
22244         
22245         var task = { // must defer to wait for browser to be ready
22246             run : function(){
22247                 //console.log("run task?" + this.doc.readyState);
22248                 this.assignDocWin();
22249                 if(this.doc.body || this.doc.readyState == 'complete'){
22250                     try {
22251                         this.doc.designMode="on";
22252                     } catch (e) {
22253                         return;
22254                     }
22255                     Roo.TaskMgr.stop(task);
22256                     this.initEditor.defer(10, this);
22257                 }
22258             },
22259             interval : 10,
22260             duration: 10000,
22261             scope: this
22262         };
22263         Roo.TaskMgr.start(task);
22264
22265     },
22266
22267     // private
22268     onResize : function(w, h)
22269     {
22270          Roo.log('resize: ' +w + ',' + h );
22271         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22272         if(!this.iframe){
22273             return;
22274         }
22275         if(typeof w == 'number'){
22276             
22277             this.iframe.style.width = w + 'px';
22278         }
22279         if(typeof h == 'number'){
22280             
22281             this.iframe.style.height = h + 'px';
22282             if(this.doc){
22283                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22284             }
22285         }
22286         
22287     },
22288
22289     /**
22290      * Toggles the editor between standard and source edit mode.
22291      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22292      */
22293     toggleSourceEdit : function(sourceEditMode){
22294         
22295         this.sourceEditMode = sourceEditMode === true;
22296         
22297         if(this.sourceEditMode){
22298  
22299             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22300             
22301         }else{
22302             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22303             //this.iframe.className = '';
22304             this.deferFocus();
22305         }
22306         //this.setSize(this.owner.wrap.getSize());
22307         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22308     },
22309
22310     
22311   
22312
22313     /**
22314      * Protected method that will not generally be called directly. If you need/want
22315      * custom HTML cleanup, this is the method you should override.
22316      * @param {String} html The HTML to be cleaned
22317      * return {String} The cleaned HTML
22318      */
22319     cleanHtml : function(html){
22320         html = String(html);
22321         if(html.length > 5){
22322             if(Roo.isSafari){ // strip safari nonsense
22323                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22324             }
22325         }
22326         if(html == '&nbsp;'){
22327             html = '';
22328         }
22329         return html;
22330     },
22331
22332     /**
22333      * HTML Editor -> Textarea
22334      * Protected method that will not generally be called directly. Syncs the contents
22335      * of the editor iframe with the textarea.
22336      */
22337     syncValue : function(){
22338         if(this.initialized){
22339             var bd = (this.doc.body || this.doc.documentElement);
22340             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22341             var html = bd.innerHTML;
22342             if(Roo.isSafari){
22343                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22344                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22345                 if(m && m[1]){
22346                     html = '<div style="'+m[0]+'">' + html + '</div>';
22347                 }
22348             }
22349             html = this.cleanHtml(html);
22350             // fix up the special chars.. normaly like back quotes in word...
22351             // however we do not want to do this with chinese..
22352             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
22353                 
22354                 var cc = match.charCodeAt();
22355
22356                 // Get the character value, handling surrogate pairs
22357                 if (match.length == 2) {
22358                     // It's a surrogate pair, calculate the Unicode code point
22359                     var high = match.charCodeAt(0) - 0xD800;
22360                     var low  = match.charCodeAt(1) - 0xDC00;
22361                     cc = (high * 0x400) + low + 0x10000;
22362                 }  else if (
22363                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22364                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22365                     (cc >= 0xf900 && cc < 0xfb00 )
22366                 ) {
22367                         return match;
22368                 }  
22369          
22370                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
22371                 return "&#" + cc + ";";
22372                 
22373                 
22374             });
22375             
22376             
22377              
22378             if(this.owner.fireEvent('beforesync', this, html) !== false){
22379                 this.el.dom.value = html;
22380                 this.owner.fireEvent('sync', this, html);
22381             }
22382         }
22383     },
22384
22385     /**
22386      * Protected method that will not generally be called directly. Pushes the value of the textarea
22387      * into the iframe editor.
22388      */
22389     pushValue : function(){
22390         if(this.initialized){
22391             var v = this.el.dom.value.trim();
22392             
22393 //            if(v.length < 1){
22394 //                v = '&#160;';
22395 //            }
22396             
22397             if(this.owner.fireEvent('beforepush', this, v) !== false){
22398                 var d = (this.doc.body || this.doc.documentElement);
22399                 d.innerHTML = v;
22400                 this.cleanUpPaste();
22401                 this.el.dom.value = d.innerHTML;
22402                 this.owner.fireEvent('push', this, v);
22403             }
22404         }
22405     },
22406
22407     // private
22408     deferFocus : function(){
22409         this.focus.defer(10, this);
22410     },
22411
22412     // doc'ed in Field
22413     focus : function(){
22414         if(this.win && !this.sourceEditMode){
22415             this.win.focus();
22416         }else{
22417             this.el.focus();
22418         }
22419     },
22420     
22421     assignDocWin: function()
22422     {
22423         var iframe = this.iframe;
22424         
22425          if(Roo.isIE){
22426             this.doc = iframe.contentWindow.document;
22427             this.win = iframe.contentWindow;
22428         } else {
22429 //            if (!Roo.get(this.frameId)) {
22430 //                return;
22431 //            }
22432 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22433 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22434             
22435             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22436                 return;
22437             }
22438             
22439             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22440             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22441         }
22442     },
22443     
22444     // private
22445     initEditor : function(){
22446         //console.log("INIT EDITOR");
22447         this.assignDocWin();
22448         
22449         
22450         
22451         this.doc.designMode="on";
22452         this.doc.open();
22453         this.doc.write(this.getDocMarkup());
22454         this.doc.close();
22455         
22456         var dbody = (this.doc.body || this.doc.documentElement);
22457         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22458         // this copies styles from the containing element into thsi one..
22459         // not sure why we need all of this..
22460         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22461         
22462         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22463         //ss['background-attachment'] = 'fixed'; // w3c
22464         dbody.bgProperties = 'fixed'; // ie
22465         //Roo.DomHelper.applyStyles(dbody, ss);
22466         Roo.EventManager.on(this.doc, {
22467             //'mousedown': this.onEditorEvent,
22468             'mouseup': this.onEditorEvent,
22469             'dblclick': this.onEditorEvent,
22470             'click': this.onEditorEvent,
22471             'keyup': this.onEditorEvent,
22472             buffer:100,
22473             scope: this
22474         });
22475         if(Roo.isGecko){
22476             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22477         }
22478         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22479             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22480         }
22481         this.initialized = true;
22482
22483         this.owner.fireEvent('initialize', this);
22484         this.pushValue();
22485     },
22486
22487     // private
22488     onDestroy : function(){
22489         
22490         
22491         
22492         if(this.rendered){
22493             
22494             //for (var i =0; i < this.toolbars.length;i++) {
22495             //    // fixme - ask toolbars for heights?
22496             //    this.toolbars[i].onDestroy();
22497            // }
22498             
22499             //this.wrap.dom.innerHTML = '';
22500             //this.wrap.remove();
22501         }
22502     },
22503
22504     // private
22505     onFirstFocus : function(){
22506         
22507         this.assignDocWin();
22508         
22509         
22510         this.activated = true;
22511          
22512     
22513         if(Roo.isGecko){ // prevent silly gecko errors
22514             this.win.focus();
22515             var s = this.win.getSelection();
22516             if(!s.focusNode || s.focusNode.nodeType != 3){
22517                 var r = s.getRangeAt(0);
22518                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22519                 r.collapse(true);
22520                 this.deferFocus();
22521             }
22522             try{
22523                 this.execCmd('useCSS', true);
22524                 this.execCmd('styleWithCSS', false);
22525             }catch(e){}
22526         }
22527         this.owner.fireEvent('activate', this);
22528     },
22529
22530     // private
22531     adjustFont: function(btn){
22532         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22533         //if(Roo.isSafari){ // safari
22534         //    adjust *= 2;
22535        // }
22536         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22537         if(Roo.isSafari){ // safari
22538             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22539             v =  (v < 10) ? 10 : v;
22540             v =  (v > 48) ? 48 : v;
22541             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22542             
22543         }
22544         
22545         
22546         v = Math.max(1, v+adjust);
22547         
22548         this.execCmd('FontSize', v  );
22549     },
22550
22551     onEditorEvent : function(e)
22552     {
22553         this.owner.fireEvent('editorevent', this, e);
22554       //  this.updateToolbar();
22555         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22556     },
22557
22558     insertTag : function(tg)
22559     {
22560         // could be a bit smarter... -> wrap the current selected tRoo..
22561         if (tg.toLowerCase() == 'span' ||
22562             tg.toLowerCase() == 'code' ||
22563             tg.toLowerCase() == 'sup' ||
22564             tg.toLowerCase() == 'sub' 
22565             ) {
22566             
22567             range = this.createRange(this.getSelection());
22568             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22569             wrappingNode.appendChild(range.extractContents());
22570             range.insertNode(wrappingNode);
22571
22572             return;
22573             
22574             
22575             
22576         }
22577         this.execCmd("formatblock",   tg);
22578         
22579     },
22580     
22581     insertText : function(txt)
22582     {
22583         
22584         
22585         var range = this.createRange();
22586         range.deleteContents();
22587                //alert(Sender.getAttribute('label'));
22588                
22589         range.insertNode(this.doc.createTextNode(txt));
22590     } ,
22591     
22592      
22593
22594     /**
22595      * Executes a Midas editor command on the editor document and performs necessary focus and
22596      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22597      * @param {String} cmd The Midas command
22598      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22599      */
22600     relayCmd : function(cmd, value){
22601         this.win.focus();
22602         this.execCmd(cmd, value);
22603         this.owner.fireEvent('editorevent', this);
22604         //this.updateToolbar();
22605         this.owner.deferFocus();
22606     },
22607
22608     /**
22609      * Executes a Midas editor command directly on the editor document.
22610      * For visual commands, you should use {@link #relayCmd} instead.
22611      * <b>This should only be called after the editor is initialized.</b>
22612      * @param {String} cmd The Midas command
22613      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22614      */
22615     execCmd : function(cmd, value){
22616         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22617         this.syncValue();
22618     },
22619  
22620  
22621    
22622     /**
22623      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22624      * to insert tRoo.
22625      * @param {String} text | dom node.. 
22626      */
22627     insertAtCursor : function(text)
22628     {
22629         
22630         if(!this.activated){
22631             return;
22632         }
22633         /*
22634         if(Roo.isIE){
22635             this.win.focus();
22636             var r = this.doc.selection.createRange();
22637             if(r){
22638                 r.collapse(true);
22639                 r.pasteHTML(text);
22640                 this.syncValue();
22641                 this.deferFocus();
22642             
22643             }
22644             return;
22645         }
22646         */
22647         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22648             this.win.focus();
22649             
22650             
22651             // from jquery ui (MIT licenced)
22652             var range, node;
22653             var win = this.win;
22654             
22655             if (win.getSelection && win.getSelection().getRangeAt) {
22656                 range = win.getSelection().getRangeAt(0);
22657                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22658                 range.insertNode(node);
22659             } else if (win.document.selection && win.document.selection.createRange) {
22660                 // no firefox support
22661                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22662                 win.document.selection.createRange().pasteHTML(txt);
22663             } else {
22664                 // no firefox support
22665                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22666                 this.execCmd('InsertHTML', txt);
22667             } 
22668             
22669             this.syncValue();
22670             
22671             this.deferFocus();
22672         }
22673     },
22674  // private
22675     mozKeyPress : function(e){
22676         if(e.ctrlKey){
22677             var c = e.getCharCode(), cmd;
22678           
22679             if(c > 0){
22680                 c = String.fromCharCode(c).toLowerCase();
22681                 switch(c){
22682                     case 'b':
22683                         cmd = 'bold';
22684                         break;
22685                     case 'i':
22686                         cmd = 'italic';
22687                         break;
22688                     
22689                     case 'u':
22690                         cmd = 'underline';
22691                         break;
22692                     
22693                     case 'v':
22694                         this.cleanUpPaste.defer(100, this);
22695                         return;
22696                         
22697                 }
22698                 if(cmd){
22699                     this.win.focus();
22700                     this.execCmd(cmd);
22701                     this.deferFocus();
22702                     e.preventDefault();
22703                 }
22704                 
22705             }
22706         }
22707     },
22708
22709     // private
22710     fixKeys : function(){ // load time branching for fastest keydown performance
22711         if(Roo.isIE){
22712             return function(e){
22713                 var k = e.getKey(), r;
22714                 if(k == e.TAB){
22715                     e.stopEvent();
22716                     r = this.doc.selection.createRange();
22717                     if(r){
22718                         r.collapse(true);
22719                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22720                         this.deferFocus();
22721                     }
22722                     return;
22723                 }
22724                 
22725                 if(k == e.ENTER){
22726                     r = this.doc.selection.createRange();
22727                     if(r){
22728                         var target = r.parentElement();
22729                         if(!target || target.tagName.toLowerCase() != 'li'){
22730                             e.stopEvent();
22731                             r.pasteHTML('<br />');
22732                             r.collapse(false);
22733                             r.select();
22734                         }
22735                     }
22736                 }
22737                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22738                     this.cleanUpPaste.defer(100, this);
22739                     return;
22740                 }
22741                 
22742                 
22743             };
22744         }else if(Roo.isOpera){
22745             return function(e){
22746                 var k = e.getKey();
22747                 if(k == e.TAB){
22748                     e.stopEvent();
22749                     this.win.focus();
22750                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22751                     this.deferFocus();
22752                 }
22753                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22754                     this.cleanUpPaste.defer(100, this);
22755                     return;
22756                 }
22757                 
22758             };
22759         }else if(Roo.isSafari){
22760             return function(e){
22761                 var k = e.getKey();
22762                 
22763                 if(k == e.TAB){
22764                     e.stopEvent();
22765                     this.execCmd('InsertText','\t');
22766                     this.deferFocus();
22767                     return;
22768                 }
22769                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22770                     this.cleanUpPaste.defer(100, this);
22771                     return;
22772                 }
22773                 
22774              };
22775         }
22776     }(),
22777     
22778     getAllAncestors: function()
22779     {
22780         var p = this.getSelectedNode();
22781         var a = [];
22782         if (!p) {
22783             a.push(p); // push blank onto stack..
22784             p = this.getParentElement();
22785         }
22786         
22787         
22788         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22789             a.push(p);
22790             p = p.parentNode;
22791         }
22792         a.push(this.doc.body);
22793         return a;
22794     },
22795     lastSel : false,
22796     lastSelNode : false,
22797     
22798     
22799     getSelection : function() 
22800     {
22801         this.assignDocWin();
22802         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22803     },
22804     
22805     getSelectedNode: function() 
22806     {
22807         // this may only work on Gecko!!!
22808         
22809         // should we cache this!!!!
22810         
22811         
22812         
22813          
22814         var range = this.createRange(this.getSelection()).cloneRange();
22815         
22816         if (Roo.isIE) {
22817             var parent = range.parentElement();
22818             while (true) {
22819                 var testRange = range.duplicate();
22820                 testRange.moveToElementText(parent);
22821                 if (testRange.inRange(range)) {
22822                     break;
22823                 }
22824                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22825                     break;
22826                 }
22827                 parent = parent.parentElement;
22828             }
22829             return parent;
22830         }
22831         
22832         // is ancestor a text element.
22833         var ac =  range.commonAncestorContainer;
22834         if (ac.nodeType == 3) {
22835             ac = ac.parentNode;
22836         }
22837         
22838         var ar = ac.childNodes;
22839          
22840         var nodes = [];
22841         var other_nodes = [];
22842         var has_other_nodes = false;
22843         for (var i=0;i<ar.length;i++) {
22844             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22845                 continue;
22846             }
22847             // fullly contained node.
22848             
22849             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22850                 nodes.push(ar[i]);
22851                 continue;
22852             }
22853             
22854             // probably selected..
22855             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22856                 other_nodes.push(ar[i]);
22857                 continue;
22858             }
22859             // outer..
22860             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22861                 continue;
22862             }
22863             
22864             
22865             has_other_nodes = true;
22866         }
22867         if (!nodes.length && other_nodes.length) {
22868             nodes= other_nodes;
22869         }
22870         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22871             return false;
22872         }
22873         
22874         return nodes[0];
22875     },
22876     createRange: function(sel)
22877     {
22878         // this has strange effects when using with 
22879         // top toolbar - not sure if it's a great idea.
22880         //this.editor.contentWindow.focus();
22881         if (typeof sel != "undefined") {
22882             try {
22883                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22884             } catch(e) {
22885                 return this.doc.createRange();
22886             }
22887         } else {
22888             return this.doc.createRange();
22889         }
22890     },
22891     getParentElement: function()
22892     {
22893         
22894         this.assignDocWin();
22895         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22896         
22897         var range = this.createRange(sel);
22898          
22899         try {
22900             var p = range.commonAncestorContainer;
22901             while (p.nodeType == 3) { // text node
22902                 p = p.parentNode;
22903             }
22904             return p;
22905         } catch (e) {
22906             return null;
22907         }
22908     
22909     },
22910     /***
22911      *
22912      * Range intersection.. the hard stuff...
22913      *  '-1' = before
22914      *  '0' = hits..
22915      *  '1' = after.
22916      *         [ -- selected range --- ]
22917      *   [fail]                        [fail]
22918      *
22919      *    basically..
22920      *      if end is before start or  hits it. fail.
22921      *      if start is after end or hits it fail.
22922      *
22923      *   if either hits (but other is outside. - then it's not 
22924      *   
22925      *    
22926      **/
22927     
22928     
22929     // @see http://www.thismuchiknow.co.uk/?p=64.
22930     rangeIntersectsNode : function(range, node)
22931     {
22932         var nodeRange = node.ownerDocument.createRange();
22933         try {
22934             nodeRange.selectNode(node);
22935         } catch (e) {
22936             nodeRange.selectNodeContents(node);
22937         }
22938     
22939         var rangeStartRange = range.cloneRange();
22940         rangeStartRange.collapse(true);
22941     
22942         var rangeEndRange = range.cloneRange();
22943         rangeEndRange.collapse(false);
22944     
22945         var nodeStartRange = nodeRange.cloneRange();
22946         nodeStartRange.collapse(true);
22947     
22948         var nodeEndRange = nodeRange.cloneRange();
22949         nodeEndRange.collapse(false);
22950     
22951         return rangeStartRange.compareBoundaryPoints(
22952                  Range.START_TO_START, nodeEndRange) == -1 &&
22953                rangeEndRange.compareBoundaryPoints(
22954                  Range.START_TO_START, nodeStartRange) == 1;
22955         
22956          
22957     },
22958     rangeCompareNode : function(range, node)
22959     {
22960         var nodeRange = node.ownerDocument.createRange();
22961         try {
22962             nodeRange.selectNode(node);
22963         } catch (e) {
22964             nodeRange.selectNodeContents(node);
22965         }
22966         
22967         
22968         range.collapse(true);
22969     
22970         nodeRange.collapse(true);
22971      
22972         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22973         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22974          
22975         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22976         
22977         var nodeIsBefore   =  ss == 1;
22978         var nodeIsAfter    = ee == -1;
22979         
22980         if (nodeIsBefore && nodeIsAfter) {
22981             return 0; // outer
22982         }
22983         if (!nodeIsBefore && nodeIsAfter) {
22984             return 1; //right trailed.
22985         }
22986         
22987         if (nodeIsBefore && !nodeIsAfter) {
22988             return 2;  // left trailed.
22989         }
22990         // fully contined.
22991         return 3;
22992     },
22993
22994     // private? - in a new class?
22995     cleanUpPaste :  function()
22996     {
22997         // cleans up the whole document..
22998         Roo.log('cleanuppaste');
22999         
23000         this.cleanUpChildren(this.doc.body);
23001         var clean = this.cleanWordChars(this.doc.body.innerHTML);
23002         if (clean != this.doc.body.innerHTML) {
23003             this.doc.body.innerHTML = clean;
23004         }
23005         
23006     },
23007     
23008     cleanWordChars : function(input) {// change the chars to hex code
23009         var he = Roo.HtmlEditorCore;
23010         
23011         var output = input;
23012         Roo.each(he.swapCodes, function(sw) { 
23013             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23014             
23015             output = output.replace(swapper, sw[1]);
23016         });
23017         
23018         return output;
23019     },
23020     
23021     
23022     cleanUpChildren : function (n)
23023     {
23024         if (!n.childNodes.length) {
23025             return;
23026         }
23027         for (var i = n.childNodes.length-1; i > -1 ; i--) {
23028            this.cleanUpChild(n.childNodes[i]);
23029         }
23030     },
23031     
23032     
23033         
23034     
23035     cleanUpChild : function (node)
23036     {
23037         var ed = this;
23038         //console.log(node);
23039         if (node.nodeName == "#text") {
23040             // clean up silly Windows -- stuff?
23041             return; 
23042         }
23043         if (node.nodeName == "#comment") {
23044             node.parentNode.removeChild(node);
23045             // clean up silly Windows -- stuff?
23046             return; 
23047         }
23048         var lcname = node.tagName.toLowerCase();
23049         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23050         // whitelist of tags..
23051         
23052         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23053             // remove node.
23054             node.parentNode.removeChild(node);
23055             return;
23056             
23057         }
23058         
23059         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23060         
23061         // spans with no attributes - just remove them..
23062         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
23063             remove_keep_children = true;
23064         }
23065         
23066         // remove <a name=....> as rendering on yahoo mailer is borked with this.
23067         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23068         
23069         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23070         //    remove_keep_children = true;
23071         //}
23072         
23073         if (remove_keep_children) {
23074             this.cleanUpChildren(node);
23075             // inserts everything just before this node...
23076             while (node.childNodes.length) {
23077                 var cn = node.childNodes[0];
23078                 node.removeChild(cn);
23079                 node.parentNode.insertBefore(cn, node);
23080             }
23081             node.parentNode.removeChild(node);
23082             return;
23083         }
23084         
23085         if (!node.attributes || !node.attributes.length) {
23086             
23087           
23088             
23089             
23090             this.cleanUpChildren(node);
23091             return;
23092         }
23093         
23094         function cleanAttr(n,v)
23095         {
23096             
23097             if (v.match(/^\./) || v.match(/^\//)) {
23098                 return;
23099             }
23100             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23101                 return;
23102             }
23103             if (v.match(/^#/)) {
23104                 return;
23105             }
23106 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23107             node.removeAttribute(n);
23108             
23109         }
23110         
23111         var cwhite = this.cwhite;
23112         var cblack = this.cblack;
23113             
23114         function cleanStyle(n,v)
23115         {
23116             if (v.match(/expression/)) { //XSS?? should we even bother..
23117                 node.removeAttribute(n);
23118                 return;
23119             }
23120             
23121             var parts = v.split(/;/);
23122             var clean = [];
23123             
23124             Roo.each(parts, function(p) {
23125                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23126                 if (!p.length) {
23127                     return true;
23128                 }
23129                 var l = p.split(':').shift().replace(/\s+/g,'');
23130                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23131                 
23132                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23133 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23134                     //node.removeAttribute(n);
23135                     return true;
23136                 }
23137                 //Roo.log()
23138                 // only allow 'c whitelisted system attributes'
23139                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23140 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23141                     //node.removeAttribute(n);
23142                     return true;
23143                 }
23144                 
23145                 
23146                  
23147                 
23148                 clean.push(p);
23149                 return true;
23150             });
23151             if (clean.length) { 
23152                 node.setAttribute(n, clean.join(';'));
23153             } else {
23154                 node.removeAttribute(n);
23155             }
23156             
23157         }
23158         
23159         
23160         for (var i = node.attributes.length-1; i > -1 ; i--) {
23161             var a = node.attributes[i];
23162             //console.log(a);
23163             
23164             if (a.name.toLowerCase().substr(0,2)=='on')  {
23165                 node.removeAttribute(a.name);
23166                 continue;
23167             }
23168             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23169                 node.removeAttribute(a.name);
23170                 continue;
23171             }
23172             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23173                 cleanAttr(a.name,a.value); // fixme..
23174                 continue;
23175             }
23176             if (a.name == 'style') {
23177                 cleanStyle(a.name,a.value);
23178                 continue;
23179             }
23180             /// clean up MS crap..
23181             // tecnically this should be a list of valid class'es..
23182             
23183             
23184             if (a.name == 'class') {
23185                 if (a.value.match(/^Mso/)) {
23186                     node.removeAttribute('class');
23187                 }
23188                 
23189                 if (a.value.match(/^body$/)) {
23190                     node.removeAttribute('class');
23191                 }
23192                 continue;
23193             }
23194             
23195             // style cleanup!?
23196             // class cleanup?
23197             
23198         }
23199         
23200         
23201         this.cleanUpChildren(node);
23202         
23203         
23204     },
23205     
23206     /**
23207      * Clean up MS wordisms...
23208      */
23209     cleanWord : function(node)
23210     {
23211         if (!node) {
23212             this.cleanWord(this.doc.body);
23213             return;
23214         }
23215         
23216         if(
23217                 node.nodeName == 'SPAN' &&
23218                 !node.hasAttributes() &&
23219                 node.childNodes.length == 1 &&
23220                 node.firstChild.nodeName == "#text"  
23221         ) {
23222             var textNode = node.firstChild;
23223             node.removeChild(textNode);
23224             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
23225                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
23226             }
23227             node.parentNode.insertBefore(textNode, node);
23228             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
23229                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
23230             }
23231             node.parentNode.removeChild(node);
23232         }
23233         
23234         if (node.nodeName == "#text") {
23235             // clean up silly Windows -- stuff?
23236             return; 
23237         }
23238         if (node.nodeName == "#comment") {
23239             node.parentNode.removeChild(node);
23240             // clean up silly Windows -- stuff?
23241             return; 
23242         }
23243         
23244         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23245             node.parentNode.removeChild(node);
23246             return;
23247         }
23248         //Roo.log(node.tagName);
23249         // remove - but keep children..
23250         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
23251             //Roo.log('-- removed');
23252             while (node.childNodes.length) {
23253                 var cn = node.childNodes[0];
23254                 node.removeChild(cn);
23255                 node.parentNode.insertBefore(cn, node);
23256                 // move node to parent - and clean it..
23257                 this.cleanWord(cn);
23258             }
23259             node.parentNode.removeChild(node);
23260             /// no need to iterate chidlren = it's got none..
23261             //this.iterateChildren(node, this.cleanWord);
23262             return;
23263         }
23264         // clean styles
23265         if (node.className.length) {
23266             
23267             var cn = node.className.split(/\W+/);
23268             var cna = [];
23269             Roo.each(cn, function(cls) {
23270                 if (cls.match(/Mso[a-zA-Z]+/)) {
23271                     return;
23272                 }
23273                 cna.push(cls);
23274             });
23275             node.className = cna.length ? cna.join(' ') : '';
23276             if (!cna.length) {
23277                 node.removeAttribute("class");
23278             }
23279         }
23280         
23281         if (node.hasAttribute("lang")) {
23282             node.removeAttribute("lang");
23283         }
23284         
23285         if (node.hasAttribute("style")) {
23286             
23287             var styles = node.getAttribute("style").split(";");
23288             var nstyle = [];
23289             Roo.each(styles, function(s) {
23290                 if (!s.match(/:/)) {
23291                     return;
23292                 }
23293                 var kv = s.split(":");
23294                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23295                     return;
23296                 }
23297                 // what ever is left... we allow.
23298                 nstyle.push(s);
23299             });
23300             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23301             if (!nstyle.length) {
23302                 node.removeAttribute('style');
23303             }
23304         }
23305         this.iterateChildren(node, this.cleanWord);
23306         
23307         
23308         
23309     },
23310     /**
23311      * iterateChildren of a Node, calling fn each time, using this as the scole..
23312      * @param {DomNode} node node to iterate children of.
23313      * @param {Function} fn method of this class to call on each item.
23314      */
23315     iterateChildren : function(node, fn)
23316     {
23317         if (!node.childNodes.length) {
23318                 return;
23319         }
23320         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23321            fn.call(this, node.childNodes[i])
23322         }
23323     },
23324     
23325     
23326     /**
23327      * cleanTableWidths.
23328      *
23329      * Quite often pasting from word etc.. results in tables with column and widths.
23330      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23331      *
23332      */
23333     cleanTableWidths : function(node)
23334     {
23335          
23336          
23337         if (!node) {
23338             this.cleanTableWidths(this.doc.body);
23339             return;
23340         }
23341         
23342         // ignore list...
23343         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23344             return; 
23345         }
23346         Roo.log(node.tagName);
23347         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23348             this.iterateChildren(node, this.cleanTableWidths);
23349             return;
23350         }
23351         if (node.hasAttribute('width')) {
23352             node.removeAttribute('width');
23353         }
23354         
23355          
23356         if (node.hasAttribute("style")) {
23357             // pretty basic...
23358             
23359             var styles = node.getAttribute("style").split(";");
23360             var nstyle = [];
23361             Roo.each(styles, function(s) {
23362                 if (!s.match(/:/)) {
23363                     return;
23364                 }
23365                 var kv = s.split(":");
23366                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23367                     return;
23368                 }
23369                 // what ever is left... we allow.
23370                 nstyle.push(s);
23371             });
23372             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23373             if (!nstyle.length) {
23374                 node.removeAttribute('style');
23375             }
23376         }
23377         
23378         this.iterateChildren(node, this.cleanTableWidths);
23379         
23380         
23381     },
23382     
23383     
23384     
23385     
23386     domToHTML : function(currentElement, depth, nopadtext) {
23387         
23388         depth = depth || 0;
23389         nopadtext = nopadtext || false;
23390     
23391         if (!currentElement) {
23392             return this.domToHTML(this.doc.body);
23393         }
23394         
23395         //Roo.log(currentElement);
23396         var j;
23397         var allText = false;
23398         var nodeName = currentElement.nodeName;
23399         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23400         
23401         if  (nodeName == '#text') {
23402             
23403             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23404         }
23405         
23406         
23407         var ret = '';
23408         if (nodeName != 'BODY') {
23409              
23410             var i = 0;
23411             // Prints the node tagName, such as <A>, <IMG>, etc
23412             if (tagName) {
23413                 var attr = [];
23414                 for(i = 0; i < currentElement.attributes.length;i++) {
23415                     // quoting?
23416                     var aname = currentElement.attributes.item(i).name;
23417                     if (!currentElement.attributes.item(i).value.length) {
23418                         continue;
23419                     }
23420                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23421                 }
23422                 
23423                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23424             } 
23425             else {
23426                 
23427                 // eack
23428             }
23429         } else {
23430             tagName = false;
23431         }
23432         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23433             return ret;
23434         }
23435         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23436             nopadtext = true;
23437         }
23438         
23439         
23440         // Traverse the tree
23441         i = 0;
23442         var currentElementChild = currentElement.childNodes.item(i);
23443         var allText = true;
23444         var innerHTML  = '';
23445         lastnode = '';
23446         while (currentElementChild) {
23447             // Formatting code (indent the tree so it looks nice on the screen)
23448             var nopad = nopadtext;
23449             if (lastnode == 'SPAN') {
23450                 nopad  = true;
23451             }
23452             // text
23453             if  (currentElementChild.nodeName == '#text') {
23454                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23455                 toadd = nopadtext ? toadd : toadd.trim();
23456                 if (!nopad && toadd.length > 80) {
23457                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23458                 }
23459                 innerHTML  += toadd;
23460                 
23461                 i++;
23462                 currentElementChild = currentElement.childNodes.item(i);
23463                 lastNode = '';
23464                 continue;
23465             }
23466             allText = false;
23467             
23468             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23469                 
23470             // Recursively traverse the tree structure of the child node
23471             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23472             lastnode = currentElementChild.nodeName;
23473             i++;
23474             currentElementChild=currentElement.childNodes.item(i);
23475         }
23476         
23477         ret += innerHTML;
23478         
23479         if (!allText) {
23480                 // The remaining code is mostly for formatting the tree
23481             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23482         }
23483         
23484         
23485         if (tagName) {
23486             ret+= "</"+tagName+">";
23487         }
23488         return ret;
23489         
23490     },
23491         
23492     applyBlacklists : function()
23493     {
23494         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23495         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23496         
23497         this.white = [];
23498         this.black = [];
23499         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23500             if (b.indexOf(tag) > -1) {
23501                 return;
23502             }
23503             this.white.push(tag);
23504             
23505         }, this);
23506         
23507         Roo.each(w, function(tag) {
23508             if (b.indexOf(tag) > -1) {
23509                 return;
23510             }
23511             if (this.white.indexOf(tag) > -1) {
23512                 return;
23513             }
23514             this.white.push(tag);
23515             
23516         }, this);
23517         
23518         
23519         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23520             if (w.indexOf(tag) > -1) {
23521                 return;
23522             }
23523             this.black.push(tag);
23524             
23525         }, this);
23526         
23527         Roo.each(b, function(tag) {
23528             if (w.indexOf(tag) > -1) {
23529                 return;
23530             }
23531             if (this.black.indexOf(tag) > -1) {
23532                 return;
23533             }
23534             this.black.push(tag);
23535             
23536         }, this);
23537         
23538         
23539         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23540         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23541         
23542         this.cwhite = [];
23543         this.cblack = [];
23544         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23545             if (b.indexOf(tag) > -1) {
23546                 return;
23547             }
23548             this.cwhite.push(tag);
23549             
23550         }, this);
23551         
23552         Roo.each(w, function(tag) {
23553             if (b.indexOf(tag) > -1) {
23554                 return;
23555             }
23556             if (this.cwhite.indexOf(tag) > -1) {
23557                 return;
23558             }
23559             this.cwhite.push(tag);
23560             
23561         }, this);
23562         
23563         
23564         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23565             if (w.indexOf(tag) > -1) {
23566                 return;
23567             }
23568             this.cblack.push(tag);
23569             
23570         }, this);
23571         
23572         Roo.each(b, function(tag) {
23573             if (w.indexOf(tag) > -1) {
23574                 return;
23575             }
23576             if (this.cblack.indexOf(tag) > -1) {
23577                 return;
23578             }
23579             this.cblack.push(tag);
23580             
23581         }, this);
23582     },
23583     
23584     setStylesheets : function(stylesheets)
23585     {
23586         if(typeof(stylesheets) == 'string'){
23587             Roo.get(this.iframe.contentDocument.head).createChild({
23588                 tag : 'link',
23589                 rel : 'stylesheet',
23590                 type : 'text/css',
23591                 href : stylesheets
23592             });
23593             
23594             return;
23595         }
23596         var _this = this;
23597      
23598         Roo.each(stylesheets, function(s) {
23599             if(!s.length){
23600                 return;
23601             }
23602             
23603             Roo.get(_this.iframe.contentDocument.head).createChild({
23604                 tag : 'link',
23605                 rel : 'stylesheet',
23606                 type : 'text/css',
23607                 href : s
23608             });
23609         });
23610
23611         
23612     },
23613     
23614     removeStylesheets : function()
23615     {
23616         var _this = this;
23617         
23618         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23619             s.remove();
23620         });
23621     },
23622     
23623     setStyle : function(style)
23624     {
23625         Roo.get(this.iframe.contentDocument.head).createChild({
23626             tag : 'style',
23627             type : 'text/css',
23628             html : style
23629         });
23630
23631         return;
23632     }
23633     
23634     // hide stuff that is not compatible
23635     /**
23636      * @event blur
23637      * @hide
23638      */
23639     /**
23640      * @event change
23641      * @hide
23642      */
23643     /**
23644      * @event focus
23645      * @hide
23646      */
23647     /**
23648      * @event specialkey
23649      * @hide
23650      */
23651     /**
23652      * @cfg {String} fieldClass @hide
23653      */
23654     /**
23655      * @cfg {String} focusClass @hide
23656      */
23657     /**
23658      * @cfg {String} autoCreate @hide
23659      */
23660     /**
23661      * @cfg {String} inputType @hide
23662      */
23663     /**
23664      * @cfg {String} invalidClass @hide
23665      */
23666     /**
23667      * @cfg {String} invalidText @hide
23668      */
23669     /**
23670      * @cfg {String} msgFx @hide
23671      */
23672     /**
23673      * @cfg {String} validateOnBlur @hide
23674      */
23675 });
23676
23677 Roo.HtmlEditorCore.white = [
23678         'area', 'br', 'img', 'input', 'hr', 'wbr',
23679         
23680        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23681        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23682        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23683        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23684        'table',   'ul',         'xmp', 
23685        
23686        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23687       'thead',   'tr', 
23688      
23689       'dir', 'menu', 'ol', 'ul', 'dl',
23690        
23691       'embed',  'object'
23692 ];
23693
23694
23695 Roo.HtmlEditorCore.black = [
23696     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23697         'applet', // 
23698         'base',   'basefont', 'bgsound', 'blink',  'body', 
23699         'frame',  'frameset', 'head',    'html',   'ilayer', 
23700         'iframe', 'layer',  'link',     'meta',    'object',   
23701         'script', 'style' ,'title',  'xml' // clean later..
23702 ];
23703 Roo.HtmlEditorCore.clean = [
23704     'script', 'style', 'title', 'xml'
23705 ];
23706 Roo.HtmlEditorCore.remove = [
23707     'font'
23708 ];
23709 // attributes..
23710
23711 Roo.HtmlEditorCore.ablack = [
23712     'on'
23713 ];
23714     
23715 Roo.HtmlEditorCore.aclean = [ 
23716     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23717 ];
23718
23719 // protocols..
23720 Roo.HtmlEditorCore.pwhite= [
23721         'http',  'https',  'mailto'
23722 ];
23723
23724 // white listed style attributes.
23725 Roo.HtmlEditorCore.cwhite= [
23726       //  'text-align', /// default is to allow most things..
23727       
23728          
23729 //        'font-size'//??
23730 ];
23731
23732 // black listed style attributes.
23733 Roo.HtmlEditorCore.cblack= [
23734       //  'font-size' -- this can be set by the project 
23735 ];
23736
23737
23738 Roo.HtmlEditorCore.swapCodes   =[ 
23739     [    8211, "--" ], 
23740     [    8212, "--" ], 
23741     [    8216,  "'" ],  
23742     [    8217, "'" ],  
23743     [    8220, '"' ],  
23744     [    8221, '"' ],  
23745     [    8226, "*" ],  
23746     [    8230, "..." ]
23747 ]; 
23748
23749     /*
23750  * - LGPL
23751  *
23752  * HtmlEditor
23753  * 
23754  */
23755
23756 /**
23757  * @class Roo.bootstrap.HtmlEditor
23758  * @extends Roo.bootstrap.TextArea
23759  * Bootstrap HtmlEditor class
23760
23761  * @constructor
23762  * Create a new HtmlEditor
23763  * @param {Object} config The config object
23764  */
23765
23766 Roo.bootstrap.HtmlEditor = function(config){
23767     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23768     if (!this.toolbars) {
23769         this.toolbars = [];
23770     }
23771     
23772     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23773     this.addEvents({
23774             /**
23775              * @event initialize
23776              * Fires when the editor is fully initialized (including the iframe)
23777              * @param {HtmlEditor} this
23778              */
23779             initialize: true,
23780             /**
23781              * @event activate
23782              * Fires when the editor is first receives the focus. Any insertion must wait
23783              * until after this event.
23784              * @param {HtmlEditor} this
23785              */
23786             activate: true,
23787              /**
23788              * @event beforesync
23789              * Fires before the textarea is updated with content from the editor iframe. Return false
23790              * to cancel the sync.
23791              * @param {HtmlEditor} this
23792              * @param {String} html
23793              */
23794             beforesync: true,
23795              /**
23796              * @event beforepush
23797              * Fires before the iframe editor is updated with content from the textarea. Return false
23798              * to cancel the push.
23799              * @param {HtmlEditor} this
23800              * @param {String} html
23801              */
23802             beforepush: true,
23803              /**
23804              * @event sync
23805              * Fires when the textarea is updated with content from the editor iframe.
23806              * @param {HtmlEditor} this
23807              * @param {String} html
23808              */
23809             sync: true,
23810              /**
23811              * @event push
23812              * Fires when the iframe editor is updated with content from the textarea.
23813              * @param {HtmlEditor} this
23814              * @param {String} html
23815              */
23816             push: true,
23817              /**
23818              * @event editmodechange
23819              * Fires when the editor switches edit modes
23820              * @param {HtmlEditor} this
23821              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23822              */
23823             editmodechange: true,
23824             /**
23825              * @event editorevent
23826              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23827              * @param {HtmlEditor} this
23828              */
23829             editorevent: true,
23830             /**
23831              * @event firstfocus
23832              * Fires when on first focus - needed by toolbars..
23833              * @param {HtmlEditor} this
23834              */
23835             firstfocus: true,
23836             /**
23837              * @event autosave
23838              * Auto save the htmlEditor value as a file into Events
23839              * @param {HtmlEditor} this
23840              */
23841             autosave: true,
23842             /**
23843              * @event savedpreview
23844              * preview the saved version of htmlEditor
23845              * @param {HtmlEditor} this
23846              */
23847             savedpreview: true
23848         });
23849 };
23850
23851
23852 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23853     
23854     
23855       /**
23856      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23857      */
23858     toolbars : false,
23859     
23860      /**
23861     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23862     */
23863     btns : [],
23864    
23865      /**
23866      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23867      *                        Roo.resizable.
23868      */
23869     resizable : false,
23870      /**
23871      * @cfg {Number} height (in pixels)
23872      */   
23873     height: 300,
23874    /**
23875      * @cfg {Number} width (in pixels)
23876      */   
23877     width: false,
23878     
23879     /**
23880      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23881      * 
23882      */
23883     stylesheets: false,
23884     
23885     // id of frame..
23886     frameId: false,
23887     
23888     // private properties
23889     validationEvent : false,
23890     deferHeight: true,
23891     initialized : false,
23892     activated : false,
23893     
23894     onFocus : Roo.emptyFn,
23895     iframePad:3,
23896     hideMode:'offsets',
23897     
23898     tbContainer : false,
23899     
23900     bodyCls : '',
23901     
23902     toolbarContainer :function() {
23903         return this.wrap.select('.x-html-editor-tb',true).first();
23904     },
23905
23906     /**
23907      * Protected method that will not generally be called directly. It
23908      * is called when the editor creates its toolbar. Override this method if you need to
23909      * add custom toolbar buttons.
23910      * @param {HtmlEditor} editor
23911      */
23912     createToolbar : function(){
23913         Roo.log('renewing');
23914         Roo.log("create toolbars");
23915         
23916         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23917         this.toolbars[0].render(this.toolbarContainer());
23918         
23919         return;
23920         
23921 //        if (!editor.toolbars || !editor.toolbars.length) {
23922 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23923 //        }
23924 //        
23925 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23926 //            editor.toolbars[i] = Roo.factory(
23927 //                    typeof(editor.toolbars[i]) == 'string' ?
23928 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23929 //                Roo.bootstrap.HtmlEditor);
23930 //            editor.toolbars[i].init(editor);
23931 //        }
23932     },
23933
23934      
23935     // private
23936     onRender : function(ct, position)
23937     {
23938        // Roo.log("Call onRender: " + this.xtype);
23939         var _t = this;
23940         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23941       
23942         this.wrap = this.inputEl().wrap({
23943             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23944         });
23945         
23946         this.editorcore.onRender(ct, position);
23947          
23948         if (this.resizable) {
23949             this.resizeEl = new Roo.Resizable(this.wrap, {
23950                 pinned : true,
23951                 wrap: true,
23952                 dynamic : true,
23953                 minHeight : this.height,
23954                 height: this.height,
23955                 handles : this.resizable,
23956                 width: this.width,
23957                 listeners : {
23958                     resize : function(r, w, h) {
23959                         _t.onResize(w,h); // -something
23960                     }
23961                 }
23962             });
23963             
23964         }
23965         this.createToolbar(this);
23966        
23967         
23968         if(!this.width && this.resizable){
23969             this.setSize(this.wrap.getSize());
23970         }
23971         if (this.resizeEl) {
23972             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23973             // should trigger onReize..
23974         }
23975         
23976     },
23977
23978     // private
23979     onResize : function(w, h)
23980     {
23981         Roo.log('resize: ' +w + ',' + h );
23982         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23983         var ew = false;
23984         var eh = false;
23985         
23986         if(this.inputEl() ){
23987             if(typeof w == 'number'){
23988                 var aw = w - this.wrap.getFrameWidth('lr');
23989                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23990                 ew = aw;
23991             }
23992             if(typeof h == 'number'){
23993                  var tbh = -11;  // fixme it needs to tool bar size!
23994                 for (var i =0; i < this.toolbars.length;i++) {
23995                     // fixme - ask toolbars for heights?
23996                     tbh += this.toolbars[i].el.getHeight();
23997                     //if (this.toolbars[i].footer) {
23998                     //    tbh += this.toolbars[i].footer.el.getHeight();
23999                     //}
24000                 }
24001               
24002                 
24003                 
24004                 
24005                 
24006                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24007                 ah -= 5; // knock a few pixes off for look..
24008                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24009                 var eh = ah;
24010             }
24011         }
24012         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24013         this.editorcore.onResize(ew,eh);
24014         
24015     },
24016
24017     /**
24018      * Toggles the editor between standard and source edit mode.
24019      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24020      */
24021     toggleSourceEdit : function(sourceEditMode)
24022     {
24023         this.editorcore.toggleSourceEdit(sourceEditMode);
24024         
24025         if(this.editorcore.sourceEditMode){
24026             Roo.log('editor - showing textarea');
24027             
24028 //            Roo.log('in');
24029 //            Roo.log(this.syncValue());
24030             this.syncValue();
24031             this.inputEl().removeClass(['hide', 'x-hidden']);
24032             this.inputEl().dom.removeAttribute('tabIndex');
24033             this.inputEl().focus();
24034         }else{
24035             Roo.log('editor - hiding textarea');
24036 //            Roo.log('out')
24037 //            Roo.log(this.pushValue()); 
24038             this.pushValue();
24039             
24040             this.inputEl().addClass(['hide', 'x-hidden']);
24041             this.inputEl().dom.setAttribute('tabIndex', -1);
24042             //this.deferFocus();
24043         }
24044          
24045         if(this.resizable){
24046             this.setSize(this.wrap.getSize());
24047         }
24048         
24049         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24050     },
24051  
24052     // private (for BoxComponent)
24053     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24054
24055     // private (for BoxComponent)
24056     getResizeEl : function(){
24057         return this.wrap;
24058     },
24059
24060     // private (for BoxComponent)
24061     getPositionEl : function(){
24062         return this.wrap;
24063     },
24064
24065     // private
24066     initEvents : function(){
24067         this.originalValue = this.getValue();
24068     },
24069
24070 //    /**
24071 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24072 //     * @method
24073 //     */
24074 //    markInvalid : Roo.emptyFn,
24075 //    /**
24076 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24077 //     * @method
24078 //     */
24079 //    clearInvalid : Roo.emptyFn,
24080
24081     setValue : function(v){
24082         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24083         this.editorcore.pushValue();
24084     },
24085
24086      
24087     // private
24088     deferFocus : function(){
24089         this.focus.defer(10, this);
24090     },
24091
24092     // doc'ed in Field
24093     focus : function(){
24094         this.editorcore.focus();
24095         
24096     },
24097       
24098
24099     // private
24100     onDestroy : function(){
24101         
24102         
24103         
24104         if(this.rendered){
24105             
24106             for (var i =0; i < this.toolbars.length;i++) {
24107                 // fixme - ask toolbars for heights?
24108                 this.toolbars[i].onDestroy();
24109             }
24110             
24111             this.wrap.dom.innerHTML = '';
24112             this.wrap.remove();
24113         }
24114     },
24115
24116     // private
24117     onFirstFocus : function(){
24118         //Roo.log("onFirstFocus");
24119         this.editorcore.onFirstFocus();
24120          for (var i =0; i < this.toolbars.length;i++) {
24121             this.toolbars[i].onFirstFocus();
24122         }
24123         
24124     },
24125     
24126     // private
24127     syncValue : function()
24128     {   
24129         this.editorcore.syncValue();
24130     },
24131     
24132     pushValue : function()
24133     {   
24134         this.editorcore.pushValue();
24135     }
24136      
24137     
24138     // hide stuff that is not compatible
24139     /**
24140      * @event blur
24141      * @hide
24142      */
24143     /**
24144      * @event change
24145      * @hide
24146      */
24147     /**
24148      * @event focus
24149      * @hide
24150      */
24151     /**
24152      * @event specialkey
24153      * @hide
24154      */
24155     /**
24156      * @cfg {String} fieldClass @hide
24157      */
24158     /**
24159      * @cfg {String} focusClass @hide
24160      */
24161     /**
24162      * @cfg {String} autoCreate @hide
24163      */
24164     /**
24165      * @cfg {String} inputType @hide
24166      */
24167      
24168     /**
24169      * @cfg {String} invalidText @hide
24170      */
24171     /**
24172      * @cfg {String} msgFx @hide
24173      */
24174     /**
24175      * @cfg {String} validateOnBlur @hide
24176      */
24177 });
24178  
24179     
24180    
24181    
24182    
24183       
24184 Roo.namespace('Roo.bootstrap.htmleditor');
24185 /**
24186  * @class Roo.bootstrap.HtmlEditorToolbar1
24187  * Basic Toolbar
24188  * 
24189  * @example
24190  * Usage:
24191  *
24192  new Roo.bootstrap.HtmlEditor({
24193     ....
24194     toolbars : [
24195         new Roo.bootstrap.HtmlEditorToolbar1({
24196             disable : { fonts: 1 , format: 1, ..., ... , ...],
24197             btns : [ .... ]
24198         })
24199     }
24200      
24201  * 
24202  * @cfg {Object} disable List of elements to disable..
24203  * @cfg {Array} btns List of additional buttons.
24204  * 
24205  * 
24206  * NEEDS Extra CSS? 
24207  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24208  */
24209  
24210 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24211 {
24212     
24213     Roo.apply(this, config);
24214     
24215     // default disabled, based on 'good practice'..
24216     this.disable = this.disable || {};
24217     Roo.applyIf(this.disable, {
24218         fontSize : true,
24219         colors : true,
24220         specialElements : true
24221     });
24222     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24223     
24224     this.editor = config.editor;
24225     this.editorcore = config.editor.editorcore;
24226     
24227     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24228     
24229     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24230     // dont call parent... till later.
24231 }
24232 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24233      
24234     bar : true,
24235     
24236     editor : false,
24237     editorcore : false,
24238     
24239     
24240     formats : [
24241         "p" ,  
24242         "h1","h2","h3","h4","h5","h6", 
24243         "pre", "code", 
24244         "abbr", "acronym", "address", "cite", "samp", "var",
24245         'div','span'
24246     ],
24247     
24248     onRender : function(ct, position)
24249     {
24250        // Roo.log("Call onRender: " + this.xtype);
24251         
24252        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24253        Roo.log(this.el);
24254        this.el.dom.style.marginBottom = '0';
24255        var _this = this;
24256        var editorcore = this.editorcore;
24257        var editor= this.editor;
24258        
24259        var children = [];
24260        var btn = function(id,cmd , toggle, handler, html){
24261        
24262             var  event = toggle ? 'toggle' : 'click';
24263        
24264             var a = {
24265                 size : 'sm',
24266                 xtype: 'Button',
24267                 xns: Roo.bootstrap,
24268                 //glyphicon : id,
24269                 fa: id,
24270                 cmd : id || cmd,
24271                 enableToggle:toggle !== false,
24272                 html : html || '',
24273                 pressed : toggle ? false : null,
24274                 listeners : {}
24275             };
24276             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24277                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24278             };
24279             children.push(a);
24280             return a;
24281        }
24282        
24283     //    var cb_box = function...
24284         
24285         var style = {
24286                 xtype: 'Button',
24287                 size : 'sm',
24288                 xns: Roo.bootstrap,
24289                 fa : 'font',
24290                 //html : 'submit'
24291                 menu : {
24292                     xtype: 'Menu',
24293                     xns: Roo.bootstrap,
24294                     items:  []
24295                 }
24296         };
24297         Roo.each(this.formats, function(f) {
24298             style.menu.items.push({
24299                 xtype :'MenuItem',
24300                 xns: Roo.bootstrap,
24301                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24302                 tagname : f,
24303                 listeners : {
24304                     click : function()
24305                     {
24306                         editorcore.insertTag(this.tagname);
24307                         editor.focus();
24308                     }
24309                 }
24310                 
24311             });
24312         });
24313         children.push(style);   
24314         
24315         btn('bold',false,true);
24316         btn('italic',false,true);
24317         btn('align-left', 'justifyleft',true);
24318         btn('align-center', 'justifycenter',true);
24319         btn('align-right' , 'justifyright',true);
24320         btn('link', false, false, function(btn) {
24321             //Roo.log("create link?");
24322             var url = prompt(this.createLinkText, this.defaultLinkValue);
24323             if(url && url != 'http:/'+'/'){
24324                 this.editorcore.relayCmd('createlink', url);
24325             }
24326         }),
24327         btn('list','insertunorderedlist',true);
24328         btn('pencil', false,true, function(btn){
24329                 Roo.log(this);
24330                 this.toggleSourceEdit(btn.pressed);
24331         });
24332         
24333         if (this.editor.btns.length > 0) {
24334             for (var i = 0; i<this.editor.btns.length; i++) {
24335                 children.push(this.editor.btns[i]);
24336             }
24337         }
24338         
24339         /*
24340         var cog = {
24341                 xtype: 'Button',
24342                 size : 'sm',
24343                 xns: Roo.bootstrap,
24344                 glyphicon : 'cog',
24345                 //html : 'submit'
24346                 menu : {
24347                     xtype: 'Menu',
24348                     xns: Roo.bootstrap,
24349                     items:  []
24350                 }
24351         };
24352         
24353         cog.menu.items.push({
24354             xtype :'MenuItem',
24355             xns: Roo.bootstrap,
24356             html : Clean styles,
24357             tagname : f,
24358             listeners : {
24359                 click : function()
24360                 {
24361                     editorcore.insertTag(this.tagname);
24362                     editor.focus();
24363                 }
24364             }
24365             
24366         });
24367        */
24368         
24369          
24370        this.xtype = 'NavSimplebar';
24371         
24372         for(var i=0;i< children.length;i++) {
24373             
24374             this.buttons.add(this.addxtypeChild(children[i]));
24375             
24376         }
24377         
24378         editor.on('editorevent', this.updateToolbar, this);
24379     },
24380     onBtnClick : function(id)
24381     {
24382        this.editorcore.relayCmd(id);
24383        this.editorcore.focus();
24384     },
24385     
24386     /**
24387      * Protected method that will not generally be called directly. It triggers
24388      * a toolbar update by reading the markup state of the current selection in the editor.
24389      */
24390     updateToolbar: function(){
24391
24392         if(!this.editorcore.activated){
24393             this.editor.onFirstFocus(); // is this neeed?
24394             return;
24395         }
24396
24397         var btns = this.buttons; 
24398         var doc = this.editorcore.doc;
24399         btns.get('bold').setActive(doc.queryCommandState('bold'));
24400         btns.get('italic').setActive(doc.queryCommandState('italic'));
24401         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24402         
24403         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24404         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24405         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24406         
24407         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24408         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24409          /*
24410         
24411         var ans = this.editorcore.getAllAncestors();
24412         if (this.formatCombo) {
24413             
24414             
24415             var store = this.formatCombo.store;
24416             this.formatCombo.setValue("");
24417             for (var i =0; i < ans.length;i++) {
24418                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24419                     // select it..
24420                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24421                     break;
24422                 }
24423             }
24424         }
24425         
24426         
24427         
24428         // hides menus... - so this cant be on a menu...
24429         Roo.bootstrap.MenuMgr.hideAll();
24430         */
24431         Roo.bootstrap.MenuMgr.hideAll();
24432         //this.editorsyncValue();
24433     },
24434     onFirstFocus: function() {
24435         this.buttons.each(function(item){
24436            item.enable();
24437         });
24438     },
24439     toggleSourceEdit : function(sourceEditMode){
24440         
24441           
24442         if(sourceEditMode){
24443             Roo.log("disabling buttons");
24444            this.buttons.each( function(item){
24445                 if(item.cmd != 'pencil'){
24446                     item.disable();
24447                 }
24448             });
24449           
24450         }else{
24451             Roo.log("enabling buttons");
24452             if(this.editorcore.initialized){
24453                 this.buttons.each( function(item){
24454                     item.enable();
24455                 });
24456             }
24457             
24458         }
24459         Roo.log("calling toggole on editor");
24460         // tell the editor that it's been pressed..
24461         this.editor.toggleSourceEdit(sourceEditMode);
24462        
24463     }
24464 });
24465
24466
24467
24468
24469
24470 /**
24471  * @class Roo.bootstrap.Table.AbstractSelectionModel
24472  * @extends Roo.util.Observable
24473  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24474  * implemented by descendant classes.  This class should not be directly instantiated.
24475  * @constructor
24476  */
24477 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24478     this.locked = false;
24479     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24480 };
24481
24482
24483 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24484     /** @ignore Called by the grid automatically. Do not call directly. */
24485     init : function(grid){
24486         this.grid = grid;
24487         this.initEvents();
24488     },
24489
24490     /**
24491      * Locks the selections.
24492      */
24493     lock : function(){
24494         this.locked = true;
24495     },
24496
24497     /**
24498      * Unlocks the selections.
24499      */
24500     unlock : function(){
24501         this.locked = false;
24502     },
24503
24504     /**
24505      * Returns true if the selections are locked.
24506      * @return {Boolean}
24507      */
24508     isLocked : function(){
24509         return this.locked;
24510     }
24511 });
24512 /**
24513  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24514  * @class Roo.bootstrap.Table.RowSelectionModel
24515  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24516  * It supports multiple selections and keyboard selection/navigation. 
24517  * @constructor
24518  * @param {Object} config
24519  */
24520
24521 Roo.bootstrap.Table.RowSelectionModel = function(config){
24522     Roo.apply(this, config);
24523     this.selections = new Roo.util.MixedCollection(false, function(o){
24524         return o.id;
24525     });
24526
24527     this.last = false;
24528     this.lastActive = false;
24529
24530     this.addEvents({
24531         /**
24532              * @event selectionchange
24533              * Fires when the selection changes
24534              * @param {SelectionModel} this
24535              */
24536             "selectionchange" : true,
24537         /**
24538              * @event afterselectionchange
24539              * Fires after the selection changes (eg. by key press or clicking)
24540              * @param {SelectionModel} this
24541              */
24542             "afterselectionchange" : true,
24543         /**
24544              * @event beforerowselect
24545              * Fires when a row is selected being selected, return false to cancel.
24546              * @param {SelectionModel} this
24547              * @param {Number} rowIndex The selected index
24548              * @param {Boolean} keepExisting False if other selections will be cleared
24549              */
24550             "beforerowselect" : true,
24551         /**
24552              * @event rowselect
24553              * Fires when a row is selected.
24554              * @param {SelectionModel} this
24555              * @param {Number} rowIndex The selected index
24556              * @param {Roo.data.Record} r The record
24557              */
24558             "rowselect" : true,
24559         /**
24560              * @event rowdeselect
24561              * Fires when a row is deselected.
24562              * @param {SelectionModel} this
24563              * @param {Number} rowIndex The selected index
24564              */
24565         "rowdeselect" : true
24566     });
24567     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24568     this.locked = false;
24569  };
24570
24571 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24572     /**
24573      * @cfg {Boolean} singleSelect
24574      * True to allow selection of only one row at a time (defaults to false)
24575      */
24576     singleSelect : false,
24577
24578     // private
24579     initEvents : function()
24580     {
24581
24582         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24583         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24584         //}else{ // allow click to work like normal
24585          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24586         //}
24587         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24588         this.grid.on("rowclick", this.handleMouseDown, this);
24589         
24590         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24591             "up" : function(e){
24592                 if(!e.shiftKey){
24593                     this.selectPrevious(e.shiftKey);
24594                 }else if(this.last !== false && this.lastActive !== false){
24595                     var last = this.last;
24596                     this.selectRange(this.last,  this.lastActive-1);
24597                     this.grid.getView().focusRow(this.lastActive);
24598                     if(last !== false){
24599                         this.last = last;
24600                     }
24601                 }else{
24602                     this.selectFirstRow();
24603                 }
24604                 this.fireEvent("afterselectionchange", this);
24605             },
24606             "down" : function(e){
24607                 if(!e.shiftKey){
24608                     this.selectNext(e.shiftKey);
24609                 }else if(this.last !== false && this.lastActive !== false){
24610                     var last = this.last;
24611                     this.selectRange(this.last,  this.lastActive+1);
24612                     this.grid.getView().focusRow(this.lastActive);
24613                     if(last !== false){
24614                         this.last = last;
24615                     }
24616                 }else{
24617                     this.selectFirstRow();
24618                 }
24619                 this.fireEvent("afterselectionchange", this);
24620             },
24621             scope: this
24622         });
24623         this.grid.store.on('load', function(){
24624             this.selections.clear();
24625         },this);
24626         /*
24627         var view = this.grid.view;
24628         view.on("refresh", this.onRefresh, this);
24629         view.on("rowupdated", this.onRowUpdated, this);
24630         view.on("rowremoved", this.onRemove, this);
24631         */
24632     },
24633
24634     // private
24635     onRefresh : function()
24636     {
24637         var ds = this.grid.store, i, v = this.grid.view;
24638         var s = this.selections;
24639         s.each(function(r){
24640             if((i = ds.indexOfId(r.id)) != -1){
24641                 v.onRowSelect(i);
24642             }else{
24643                 s.remove(r);
24644             }
24645         });
24646     },
24647
24648     // private
24649     onRemove : function(v, index, r){
24650         this.selections.remove(r);
24651     },
24652
24653     // private
24654     onRowUpdated : function(v, index, r){
24655         if(this.isSelected(r)){
24656             v.onRowSelect(index);
24657         }
24658     },
24659
24660     /**
24661      * Select records.
24662      * @param {Array} records The records to select
24663      * @param {Boolean} keepExisting (optional) True to keep existing selections
24664      */
24665     selectRecords : function(records, keepExisting)
24666     {
24667         if(!keepExisting){
24668             this.clearSelections();
24669         }
24670             var ds = this.grid.store;
24671         for(var i = 0, len = records.length; i < len; i++){
24672             this.selectRow(ds.indexOf(records[i]), true);
24673         }
24674     },
24675
24676     /**
24677      * Gets the number of selected rows.
24678      * @return {Number}
24679      */
24680     getCount : function(){
24681         return this.selections.length;
24682     },
24683
24684     /**
24685      * Selects the first row in the grid.
24686      */
24687     selectFirstRow : function(){
24688         this.selectRow(0);
24689     },
24690
24691     /**
24692      * Select the last row.
24693      * @param {Boolean} keepExisting (optional) True to keep existing selections
24694      */
24695     selectLastRow : function(keepExisting){
24696         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24697         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24698     },
24699
24700     /**
24701      * Selects the row immediately following the last selected row.
24702      * @param {Boolean} keepExisting (optional) True to keep existing selections
24703      */
24704     selectNext : function(keepExisting)
24705     {
24706             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24707             this.selectRow(this.last+1, keepExisting);
24708             this.grid.getView().focusRow(this.last);
24709         }
24710     },
24711
24712     /**
24713      * Selects the row that precedes the last selected row.
24714      * @param {Boolean} keepExisting (optional) True to keep existing selections
24715      */
24716     selectPrevious : function(keepExisting){
24717         if(this.last){
24718             this.selectRow(this.last-1, keepExisting);
24719             this.grid.getView().focusRow(this.last);
24720         }
24721     },
24722
24723     /**
24724      * Returns the selected records
24725      * @return {Array} Array of selected records
24726      */
24727     getSelections : function(){
24728         return [].concat(this.selections.items);
24729     },
24730
24731     /**
24732      * Returns the first selected record.
24733      * @return {Record}
24734      */
24735     getSelected : function(){
24736         return this.selections.itemAt(0);
24737     },
24738
24739
24740     /**
24741      * Clears all selections.
24742      */
24743     clearSelections : function(fast)
24744     {
24745         if(this.locked) {
24746             return;
24747         }
24748         if(fast !== true){
24749                 var ds = this.grid.store;
24750             var s = this.selections;
24751             s.each(function(r){
24752                 this.deselectRow(ds.indexOfId(r.id));
24753             }, this);
24754             s.clear();
24755         }else{
24756             this.selections.clear();
24757         }
24758         this.last = false;
24759     },
24760
24761
24762     /**
24763      * Selects all rows.
24764      */
24765     selectAll : function(){
24766         if(this.locked) {
24767             return;
24768         }
24769         this.selections.clear();
24770         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24771             this.selectRow(i, true);
24772         }
24773     },
24774
24775     /**
24776      * Returns True if there is a selection.
24777      * @return {Boolean}
24778      */
24779     hasSelection : function(){
24780         return this.selections.length > 0;
24781     },
24782
24783     /**
24784      * Returns True if the specified row is selected.
24785      * @param {Number/Record} record The record or index of the record to check
24786      * @return {Boolean}
24787      */
24788     isSelected : function(index){
24789             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24790         return (r && this.selections.key(r.id) ? true : false);
24791     },
24792
24793     /**
24794      * Returns True if the specified record id is selected.
24795      * @param {String} id The id of record to check
24796      * @return {Boolean}
24797      */
24798     isIdSelected : function(id){
24799         return (this.selections.key(id) ? true : false);
24800     },
24801
24802
24803     // private
24804     handleMouseDBClick : function(e, t){
24805         
24806     },
24807     // private
24808     handleMouseDown : function(e, t)
24809     {
24810             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24811         if(this.isLocked() || rowIndex < 0 ){
24812             return;
24813         };
24814         if(e.shiftKey && this.last !== false){
24815             var last = this.last;
24816             this.selectRange(last, rowIndex, e.ctrlKey);
24817             this.last = last; // reset the last
24818             t.focus();
24819     
24820         }else{
24821             var isSelected = this.isSelected(rowIndex);
24822             //Roo.log("select row:" + rowIndex);
24823             if(isSelected){
24824                 this.deselectRow(rowIndex);
24825             } else {
24826                         this.selectRow(rowIndex, true);
24827             }
24828     
24829             /*
24830                 if(e.button !== 0 && isSelected){
24831                 alert('rowIndex 2: ' + rowIndex);
24832                     view.focusRow(rowIndex);
24833                 }else if(e.ctrlKey && isSelected){
24834                     this.deselectRow(rowIndex);
24835                 }else if(!isSelected){
24836                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24837                     view.focusRow(rowIndex);
24838                 }
24839             */
24840         }
24841         this.fireEvent("afterselectionchange", this);
24842     },
24843     // private
24844     handleDragableRowClick :  function(grid, rowIndex, e) 
24845     {
24846         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24847             this.selectRow(rowIndex, false);
24848             grid.view.focusRow(rowIndex);
24849              this.fireEvent("afterselectionchange", this);
24850         }
24851     },
24852     
24853     /**
24854      * Selects multiple rows.
24855      * @param {Array} rows Array of the indexes of the row to select
24856      * @param {Boolean} keepExisting (optional) True to keep existing selections
24857      */
24858     selectRows : function(rows, keepExisting){
24859         if(!keepExisting){
24860             this.clearSelections();
24861         }
24862         for(var i = 0, len = rows.length; i < len; i++){
24863             this.selectRow(rows[i], true);
24864         }
24865     },
24866
24867     /**
24868      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24869      * @param {Number} startRow The index of the first row in the range
24870      * @param {Number} endRow The index of the last row in the range
24871      * @param {Boolean} keepExisting (optional) True to retain existing selections
24872      */
24873     selectRange : function(startRow, endRow, keepExisting){
24874         if(this.locked) {
24875             return;
24876         }
24877         if(!keepExisting){
24878             this.clearSelections();
24879         }
24880         if(startRow <= endRow){
24881             for(var i = startRow; i <= endRow; i++){
24882                 this.selectRow(i, true);
24883             }
24884         }else{
24885             for(var i = startRow; i >= endRow; i--){
24886                 this.selectRow(i, true);
24887             }
24888         }
24889     },
24890
24891     /**
24892      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24893      * @param {Number} startRow The index of the first row in the range
24894      * @param {Number} endRow The index of the last row in the range
24895      */
24896     deselectRange : function(startRow, endRow, preventViewNotify){
24897         if(this.locked) {
24898             return;
24899         }
24900         for(var i = startRow; i <= endRow; i++){
24901             this.deselectRow(i, preventViewNotify);
24902         }
24903     },
24904
24905     /**
24906      * Selects a row.
24907      * @param {Number} row The index of the row to select
24908      * @param {Boolean} keepExisting (optional) True to keep existing selections
24909      */
24910     selectRow : function(index, keepExisting, preventViewNotify)
24911     {
24912             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24913             return;
24914         }
24915         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24916             if(!keepExisting || this.singleSelect){
24917                 this.clearSelections();
24918             }
24919             
24920             var r = this.grid.store.getAt(index);
24921             //console.log('selectRow - record id :' + r.id);
24922             
24923             this.selections.add(r);
24924             this.last = this.lastActive = index;
24925             if(!preventViewNotify){
24926                 var proxy = new Roo.Element(
24927                                 this.grid.getRowDom(index)
24928                 );
24929                 proxy.addClass('bg-info info');
24930             }
24931             this.fireEvent("rowselect", this, index, r);
24932             this.fireEvent("selectionchange", this);
24933         }
24934     },
24935
24936     /**
24937      * Deselects a row.
24938      * @param {Number} row The index of the row to deselect
24939      */
24940     deselectRow : function(index, preventViewNotify)
24941     {
24942         if(this.locked) {
24943             return;
24944         }
24945         if(this.last == index){
24946             this.last = false;
24947         }
24948         if(this.lastActive == index){
24949             this.lastActive = false;
24950         }
24951         
24952         var r = this.grid.store.getAt(index);
24953         if (!r) {
24954             return;
24955         }
24956         
24957         this.selections.remove(r);
24958         //.console.log('deselectRow - record id :' + r.id);
24959         if(!preventViewNotify){
24960         
24961             var proxy = new Roo.Element(
24962                 this.grid.getRowDom(index)
24963             );
24964             proxy.removeClass('bg-info info');
24965         }
24966         this.fireEvent("rowdeselect", this, index);
24967         this.fireEvent("selectionchange", this);
24968     },
24969
24970     // private
24971     restoreLast : function(){
24972         if(this._last){
24973             this.last = this._last;
24974         }
24975     },
24976
24977     // private
24978     acceptsNav : function(row, col, cm){
24979         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24980     },
24981
24982     // private
24983     onEditorKey : function(field, e){
24984         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24985         if(k == e.TAB){
24986             e.stopEvent();
24987             ed.completeEdit();
24988             if(e.shiftKey){
24989                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24990             }else{
24991                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24992             }
24993         }else if(k == e.ENTER && !e.ctrlKey){
24994             e.stopEvent();
24995             ed.completeEdit();
24996             if(e.shiftKey){
24997                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24998             }else{
24999                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
25000             }
25001         }else if(k == e.ESC){
25002             ed.cancelEdit();
25003         }
25004         if(newCell){
25005             g.startEditing(newCell[0], newCell[1]);
25006         }
25007     }
25008 });
25009 /*
25010  * Based on:
25011  * Ext JS Library 1.1.1
25012  * Copyright(c) 2006-2007, Ext JS, LLC.
25013  *
25014  * Originally Released Under LGPL - original licence link has changed is not relivant.
25015  *
25016  * Fork - LGPL
25017  * <script type="text/javascript">
25018  */
25019  
25020 /**
25021  * @class Roo.bootstrap.PagingToolbar
25022  * @extends Roo.bootstrap.NavSimplebar
25023  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25024  * @constructor
25025  * Create a new PagingToolbar
25026  * @param {Object} config The config object
25027  * @param {Roo.data.Store} store
25028  */
25029 Roo.bootstrap.PagingToolbar = function(config)
25030 {
25031     // old args format still supported... - xtype is prefered..
25032         // created from xtype...
25033     
25034     this.ds = config.dataSource;
25035     
25036     if (config.store && !this.ds) {
25037         this.store= Roo.factory(config.store, Roo.data);
25038         this.ds = this.store;
25039         this.ds.xmodule = this.xmodule || false;
25040     }
25041     
25042     this.toolbarItems = [];
25043     if (config.items) {
25044         this.toolbarItems = config.items;
25045     }
25046     
25047     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25048     
25049     this.cursor = 0;
25050     
25051     if (this.ds) { 
25052         this.bind(this.ds);
25053     }
25054     
25055     if (Roo.bootstrap.version == 4) {
25056         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25057     } else {
25058         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25059     }
25060     
25061 };
25062
25063 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25064     /**
25065      * @cfg {Roo.data.Store} dataSource
25066      * The underlying data store providing the paged data
25067      */
25068     /**
25069      * @cfg {String/HTMLElement/Element} container
25070      * container The id or element that will contain the toolbar
25071      */
25072     /**
25073      * @cfg {Boolean} displayInfo
25074      * True to display the displayMsg (defaults to false)
25075      */
25076     /**
25077      * @cfg {Number} pageSize
25078      * The number of records to display per page (defaults to 20)
25079      */
25080     pageSize: 20,
25081     /**
25082      * @cfg {String} displayMsg
25083      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25084      */
25085     displayMsg : 'Displaying {0} - {1} of {2}',
25086     /**
25087      * @cfg {String} emptyMsg
25088      * The message to display when no records are found (defaults to "No data to display")
25089      */
25090     emptyMsg : 'No data to display',
25091     /**
25092      * Customizable piece of the default paging text (defaults to "Page")
25093      * @type String
25094      */
25095     beforePageText : "Page",
25096     /**
25097      * Customizable piece of the default paging text (defaults to "of %0")
25098      * @type String
25099      */
25100     afterPageText : "of {0}",
25101     /**
25102      * Customizable piece of the default paging text (defaults to "First Page")
25103      * @type String
25104      */
25105     firstText : "First Page",
25106     /**
25107      * Customizable piece of the default paging text (defaults to "Previous Page")
25108      * @type String
25109      */
25110     prevText : "Previous Page",
25111     /**
25112      * Customizable piece of the default paging text (defaults to "Next Page")
25113      * @type String
25114      */
25115     nextText : "Next Page",
25116     /**
25117      * Customizable piece of the default paging text (defaults to "Last Page")
25118      * @type String
25119      */
25120     lastText : "Last Page",
25121     /**
25122      * Customizable piece of the default paging text (defaults to "Refresh")
25123      * @type String
25124      */
25125     refreshText : "Refresh",
25126
25127     buttons : false,
25128     // private
25129     onRender : function(ct, position) 
25130     {
25131         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25132         this.navgroup.parentId = this.id;
25133         this.navgroup.onRender(this.el, null);
25134         // add the buttons to the navgroup
25135         
25136         if(this.displayInfo){
25137             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25138             this.displayEl = this.el.select('.x-paging-info', true).first();
25139 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25140 //            this.displayEl = navel.el.select('span',true).first();
25141         }
25142         
25143         var _this = this;
25144         
25145         if(this.buttons){
25146             Roo.each(_this.buttons, function(e){ // this might need to use render????
25147                Roo.factory(e).render(_this.el);
25148             });
25149         }
25150             
25151         Roo.each(_this.toolbarItems, function(e) {
25152             _this.navgroup.addItem(e);
25153         });
25154         
25155         
25156         this.first = this.navgroup.addItem({
25157             tooltip: this.firstText,
25158             cls: "prev btn-outline-secondary",
25159             html : ' <i class="fa fa-step-backward"></i>',
25160             disabled: true,
25161             preventDefault: true,
25162             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25163         });
25164         
25165         this.prev =  this.navgroup.addItem({
25166             tooltip: this.prevText,
25167             cls: "prev btn-outline-secondary",
25168             html : ' <i class="fa fa-backward"></i>',
25169             disabled: true,
25170             preventDefault: true,
25171             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25172         });
25173     //this.addSeparator();
25174         
25175         
25176         var field = this.navgroup.addItem( {
25177             tagtype : 'span',
25178             cls : 'x-paging-position  btn-outline-secondary',
25179              disabled: true,
25180             html : this.beforePageText  +
25181                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25182                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25183          } ); //?? escaped?
25184         
25185         this.field = field.el.select('input', true).first();
25186         this.field.on("keydown", this.onPagingKeydown, this);
25187         this.field.on("focus", function(){this.dom.select();});
25188     
25189     
25190         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25191         //this.field.setHeight(18);
25192         //this.addSeparator();
25193         this.next = this.navgroup.addItem({
25194             tooltip: this.nextText,
25195             cls: "next btn-outline-secondary",
25196             html : ' <i class="fa fa-forward"></i>',
25197             disabled: true,
25198             preventDefault: true,
25199             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25200         });
25201         this.last = this.navgroup.addItem({
25202             tooltip: this.lastText,
25203             html : ' <i class="fa fa-step-forward"></i>',
25204             cls: "next btn-outline-secondary",
25205             disabled: true,
25206             preventDefault: true,
25207             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25208         });
25209     //this.addSeparator();
25210         this.loading = this.navgroup.addItem({
25211             tooltip: this.refreshText,
25212             cls: "btn-outline-secondary",
25213             html : ' <i class="fa fa-refresh"></i>',
25214             preventDefault: true,
25215             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25216         });
25217         
25218     },
25219
25220     // private
25221     updateInfo : function(){
25222         if(this.displayEl){
25223             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25224             var msg = count == 0 ?
25225                 this.emptyMsg :
25226                 String.format(
25227                     this.displayMsg,
25228                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25229                 );
25230             this.displayEl.update(msg);
25231         }
25232     },
25233
25234     // private
25235     onLoad : function(ds, r, o)
25236     {
25237         this.cursor = o.params.start ? o.params.start : 0;
25238         
25239         var d = this.getPageData(),
25240             ap = d.activePage,
25241             ps = d.pages;
25242         
25243         
25244         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25245         this.field.dom.value = ap;
25246         this.first.setDisabled(ap == 1);
25247         this.prev.setDisabled(ap == 1);
25248         this.next.setDisabled(ap == ps);
25249         this.last.setDisabled(ap == ps);
25250         this.loading.enable();
25251         this.updateInfo();
25252     },
25253
25254     // private
25255     getPageData : function(){
25256         var total = this.ds.getTotalCount();
25257         return {
25258             total : total,
25259             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25260             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25261         };
25262     },
25263
25264     // private
25265     onLoadError : function(){
25266         this.loading.enable();
25267     },
25268
25269     // private
25270     onPagingKeydown : function(e){
25271         var k = e.getKey();
25272         var d = this.getPageData();
25273         if(k == e.RETURN){
25274             var v = this.field.dom.value, pageNum;
25275             if(!v || isNaN(pageNum = parseInt(v, 10))){
25276                 this.field.dom.value = d.activePage;
25277                 return;
25278             }
25279             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25280             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25281             e.stopEvent();
25282         }
25283         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))
25284         {
25285           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25286           this.field.dom.value = pageNum;
25287           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25288           e.stopEvent();
25289         }
25290         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25291         {
25292           var v = this.field.dom.value, pageNum; 
25293           var increment = (e.shiftKey) ? 10 : 1;
25294           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25295                 increment *= -1;
25296           }
25297           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25298             this.field.dom.value = d.activePage;
25299             return;
25300           }
25301           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25302           {
25303             this.field.dom.value = parseInt(v, 10) + increment;
25304             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25305             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25306           }
25307           e.stopEvent();
25308         }
25309     },
25310
25311     // private
25312     beforeLoad : function(){
25313         if(this.loading){
25314             this.loading.disable();
25315         }
25316     },
25317
25318     // private
25319     onClick : function(which){
25320         
25321         var ds = this.ds;
25322         if (!ds) {
25323             return;
25324         }
25325         
25326         switch(which){
25327             case "first":
25328                 ds.load({params:{start: 0, limit: this.pageSize}});
25329             break;
25330             case "prev":
25331                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25332             break;
25333             case "next":
25334                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25335             break;
25336             case "last":
25337                 var total = ds.getTotalCount();
25338                 var extra = total % this.pageSize;
25339                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25340                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25341             break;
25342             case "refresh":
25343                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25344             break;
25345         }
25346     },
25347
25348     /**
25349      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25350      * @param {Roo.data.Store} store The data store to unbind
25351      */
25352     unbind : function(ds){
25353         ds.un("beforeload", this.beforeLoad, this);
25354         ds.un("load", this.onLoad, this);
25355         ds.un("loadexception", this.onLoadError, this);
25356         ds.un("remove", this.updateInfo, this);
25357         ds.un("add", this.updateInfo, this);
25358         this.ds = undefined;
25359     },
25360
25361     /**
25362      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25363      * @param {Roo.data.Store} store The data store to bind
25364      */
25365     bind : function(ds){
25366         ds.on("beforeload", this.beforeLoad, this);
25367         ds.on("load", this.onLoad, this);
25368         ds.on("loadexception", this.onLoadError, this);
25369         ds.on("remove", this.updateInfo, this);
25370         ds.on("add", this.updateInfo, this);
25371         this.ds = ds;
25372     }
25373 });/*
25374  * - LGPL
25375  *
25376  * element
25377  * 
25378  */
25379
25380 /**
25381  * @class Roo.bootstrap.MessageBar
25382  * @extends Roo.bootstrap.Component
25383  * Bootstrap MessageBar class
25384  * @cfg {String} html contents of the MessageBar
25385  * @cfg {String} weight (info | success | warning | danger) default info
25386  * @cfg {String} beforeClass insert the bar before the given class
25387  * @cfg {Boolean} closable (true | false) default false
25388  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25389  * 
25390  * @constructor
25391  * Create a new Element
25392  * @param {Object} config The config object
25393  */
25394
25395 Roo.bootstrap.MessageBar = function(config){
25396     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25397 };
25398
25399 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25400     
25401     html: '',
25402     weight: 'info',
25403     closable: false,
25404     fixed: false,
25405     beforeClass: 'bootstrap-sticky-wrap',
25406     
25407     getAutoCreate : function(){
25408         
25409         var cfg = {
25410             tag: 'div',
25411             cls: 'alert alert-dismissable alert-' + this.weight,
25412             cn: [
25413                 {
25414                     tag: 'span',
25415                     cls: 'message',
25416                     html: this.html || ''
25417                 }
25418             ]
25419         };
25420         
25421         if(this.fixed){
25422             cfg.cls += ' alert-messages-fixed';
25423         }
25424         
25425         if(this.closable){
25426             cfg.cn.push({
25427                 tag: 'button',
25428                 cls: 'close',
25429                 html: 'x'
25430             });
25431         }
25432         
25433         return cfg;
25434     },
25435     
25436     onRender : function(ct, position)
25437     {
25438         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25439         
25440         if(!this.el){
25441             var cfg = Roo.apply({},  this.getAutoCreate());
25442             cfg.id = Roo.id();
25443             
25444             if (this.cls) {
25445                 cfg.cls += ' ' + this.cls;
25446             }
25447             if (this.style) {
25448                 cfg.style = this.style;
25449             }
25450             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25451             
25452             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25453         }
25454         
25455         this.el.select('>button.close').on('click', this.hide, this);
25456         
25457     },
25458     
25459     show : function()
25460     {
25461         if (!this.rendered) {
25462             this.render();
25463         }
25464         
25465         this.el.show();
25466         
25467         this.fireEvent('show', this);
25468         
25469     },
25470     
25471     hide : function()
25472     {
25473         if (!this.rendered) {
25474             this.render();
25475         }
25476         
25477         this.el.hide();
25478         
25479         this.fireEvent('hide', this);
25480     },
25481     
25482     update : function()
25483     {
25484 //        var e = this.el.dom.firstChild;
25485 //        
25486 //        if(this.closable){
25487 //            e = e.nextSibling;
25488 //        }
25489 //        
25490 //        e.data = this.html || '';
25491
25492         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25493     }
25494    
25495 });
25496
25497  
25498
25499      /*
25500  * - LGPL
25501  *
25502  * Graph
25503  * 
25504  */
25505
25506
25507 /**
25508  * @class Roo.bootstrap.Graph
25509  * @extends Roo.bootstrap.Component
25510  * Bootstrap Graph class
25511 > Prameters
25512  -sm {number} sm 4
25513  -md {number} md 5
25514  @cfg {String} graphtype  bar | vbar | pie
25515  @cfg {number} g_x coodinator | centre x (pie)
25516  @cfg {number} g_y coodinator | centre y (pie)
25517  @cfg {number} g_r radius (pie)
25518  @cfg {number} g_height height of the chart (respected by all elements in the set)
25519  @cfg {number} g_width width of the chart (respected by all elements in the set)
25520  @cfg {Object} title The title of the chart
25521     
25522  -{Array}  values
25523  -opts (object) options for the chart 
25524      o {
25525      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25526      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25527      o vgutter (number)
25528      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.
25529      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25530      o to
25531      o stretch (boolean)
25532      o }
25533  -opts (object) options for the pie
25534      o{
25535      o cut
25536      o startAngle (number)
25537      o endAngle (number)
25538      } 
25539  *
25540  * @constructor
25541  * Create a new Input
25542  * @param {Object} config The config object
25543  */
25544
25545 Roo.bootstrap.Graph = function(config){
25546     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25547     
25548     this.addEvents({
25549         // img events
25550         /**
25551          * @event click
25552          * The img click event for the img.
25553          * @param {Roo.EventObject} e
25554          */
25555         "click" : true
25556     });
25557 };
25558
25559 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25560     
25561     sm: 4,
25562     md: 5,
25563     graphtype: 'bar',
25564     g_height: 250,
25565     g_width: 400,
25566     g_x: 50,
25567     g_y: 50,
25568     g_r: 30,
25569     opts:{
25570         //g_colors: this.colors,
25571         g_type: 'soft',
25572         g_gutter: '20%'
25573
25574     },
25575     title : false,
25576
25577     getAutoCreate : function(){
25578         
25579         var cfg = {
25580             tag: 'div',
25581             html : null
25582         };
25583         
25584         
25585         return  cfg;
25586     },
25587
25588     onRender : function(ct,position){
25589         
25590         
25591         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25592         
25593         if (typeof(Raphael) == 'undefined') {
25594             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25595             return;
25596         }
25597         
25598         this.raphael = Raphael(this.el.dom);
25599         
25600                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25601                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25602                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25603                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25604                 /*
25605                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25606                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25607                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25608                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25609                 
25610                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25611                 r.barchart(330, 10, 300, 220, data1);
25612                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25613                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25614                 */
25615                 
25616                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25617                 // r.barchart(30, 30, 560, 250,  xdata, {
25618                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25619                 //     axis : "0 0 1 1",
25620                 //     axisxlabels :  xdata
25621                 //     //yvalues : cols,
25622                    
25623                 // });
25624 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25625 //        
25626 //        this.load(null,xdata,{
25627 //                axis : "0 0 1 1",
25628 //                axisxlabels :  xdata
25629 //                });
25630
25631     },
25632
25633     load : function(graphtype,xdata,opts)
25634     {
25635         this.raphael.clear();
25636         if(!graphtype) {
25637             graphtype = this.graphtype;
25638         }
25639         if(!opts){
25640             opts = this.opts;
25641         }
25642         var r = this.raphael,
25643             fin = function () {
25644                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25645             },
25646             fout = function () {
25647                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25648             },
25649             pfin = function() {
25650                 this.sector.stop();
25651                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25652
25653                 if (this.label) {
25654                     this.label[0].stop();
25655                     this.label[0].attr({ r: 7.5 });
25656                     this.label[1].attr({ "font-weight": 800 });
25657                 }
25658             },
25659             pfout = function() {
25660                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25661
25662                 if (this.label) {
25663                     this.label[0].animate({ r: 5 }, 500, "bounce");
25664                     this.label[1].attr({ "font-weight": 400 });
25665                 }
25666             };
25667
25668         switch(graphtype){
25669             case 'bar':
25670                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25671                 break;
25672             case 'hbar':
25673                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25674                 break;
25675             case 'pie':
25676 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25677 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25678 //            
25679                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25680                 
25681                 break;
25682
25683         }
25684         
25685         if(this.title){
25686             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25687         }
25688         
25689     },
25690     
25691     setTitle: function(o)
25692     {
25693         this.title = o;
25694     },
25695     
25696     initEvents: function() {
25697         
25698         if(!this.href){
25699             this.el.on('click', this.onClick, this);
25700         }
25701     },
25702     
25703     onClick : function(e)
25704     {
25705         Roo.log('img onclick');
25706         this.fireEvent('click', this, e);
25707     }
25708    
25709 });
25710
25711  
25712 /*
25713  * - LGPL
25714  *
25715  * numberBox
25716  * 
25717  */
25718 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25719
25720 /**
25721  * @class Roo.bootstrap.dash.NumberBox
25722  * @extends Roo.bootstrap.Component
25723  * Bootstrap NumberBox class
25724  * @cfg {String} headline Box headline
25725  * @cfg {String} content Box content
25726  * @cfg {String} icon Box icon
25727  * @cfg {String} footer Footer text
25728  * @cfg {String} fhref Footer href
25729  * 
25730  * @constructor
25731  * Create a new NumberBox
25732  * @param {Object} config The config object
25733  */
25734
25735
25736 Roo.bootstrap.dash.NumberBox = function(config){
25737     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25738     
25739 };
25740
25741 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25742     
25743     headline : '',
25744     content : '',
25745     icon : '',
25746     footer : '',
25747     fhref : '',
25748     ficon : '',
25749     
25750     getAutoCreate : function(){
25751         
25752         var cfg = {
25753             tag : 'div',
25754             cls : 'small-box ',
25755             cn : [
25756                 {
25757                     tag : 'div',
25758                     cls : 'inner',
25759                     cn :[
25760                         {
25761                             tag : 'h3',
25762                             cls : 'roo-headline',
25763                             html : this.headline
25764                         },
25765                         {
25766                             tag : 'p',
25767                             cls : 'roo-content',
25768                             html : this.content
25769                         }
25770                     ]
25771                 }
25772             ]
25773         };
25774         
25775         if(this.icon){
25776             cfg.cn.push({
25777                 tag : 'div',
25778                 cls : 'icon',
25779                 cn :[
25780                     {
25781                         tag : 'i',
25782                         cls : 'ion ' + this.icon
25783                     }
25784                 ]
25785             });
25786         }
25787         
25788         if(this.footer){
25789             var footer = {
25790                 tag : 'a',
25791                 cls : 'small-box-footer',
25792                 href : this.fhref || '#',
25793                 html : this.footer
25794             };
25795             
25796             cfg.cn.push(footer);
25797             
25798         }
25799         
25800         return  cfg;
25801     },
25802
25803     onRender : function(ct,position){
25804         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25805
25806
25807        
25808                 
25809     },
25810
25811     setHeadline: function (value)
25812     {
25813         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25814     },
25815     
25816     setFooter: function (value, href)
25817     {
25818         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25819         
25820         if(href){
25821             this.el.select('a.small-box-footer',true).first().attr('href', href);
25822         }
25823         
25824     },
25825
25826     setContent: function (value)
25827     {
25828         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25829     },
25830
25831     initEvents: function() 
25832     {   
25833         
25834     }
25835     
25836 });
25837
25838  
25839 /*
25840  * - LGPL
25841  *
25842  * TabBox
25843  * 
25844  */
25845 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25846
25847 /**
25848  * @class Roo.bootstrap.dash.TabBox
25849  * @extends Roo.bootstrap.Component
25850  * Bootstrap TabBox class
25851  * @cfg {String} title Title of the TabBox
25852  * @cfg {String} icon Icon of the TabBox
25853  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25854  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25855  * 
25856  * @constructor
25857  * Create a new TabBox
25858  * @param {Object} config The config object
25859  */
25860
25861
25862 Roo.bootstrap.dash.TabBox = function(config){
25863     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25864     this.addEvents({
25865         // raw events
25866         /**
25867          * @event addpane
25868          * When a pane is added
25869          * @param {Roo.bootstrap.dash.TabPane} pane
25870          */
25871         "addpane" : true,
25872         /**
25873          * @event activatepane
25874          * When a pane is activated
25875          * @param {Roo.bootstrap.dash.TabPane} pane
25876          */
25877         "activatepane" : true
25878         
25879          
25880     });
25881     
25882     this.panes = [];
25883 };
25884
25885 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25886
25887     title : '',
25888     icon : false,
25889     showtabs : true,
25890     tabScrollable : false,
25891     
25892     getChildContainer : function()
25893     {
25894         return this.el.select('.tab-content', true).first();
25895     },
25896     
25897     getAutoCreate : function(){
25898         
25899         var header = {
25900             tag: 'li',
25901             cls: 'pull-left header',
25902             html: this.title,
25903             cn : []
25904         };
25905         
25906         if(this.icon){
25907             header.cn.push({
25908                 tag: 'i',
25909                 cls: 'fa ' + this.icon
25910             });
25911         }
25912         
25913         var h = {
25914             tag: 'ul',
25915             cls: 'nav nav-tabs pull-right',
25916             cn: [
25917                 header
25918             ]
25919         };
25920         
25921         if(this.tabScrollable){
25922             h = {
25923                 tag: 'div',
25924                 cls: 'tab-header',
25925                 cn: [
25926                     {
25927                         tag: 'ul',
25928                         cls: 'nav nav-tabs pull-right',
25929                         cn: [
25930                             header
25931                         ]
25932                     }
25933                 ]
25934             };
25935         }
25936         
25937         var cfg = {
25938             tag: 'div',
25939             cls: 'nav-tabs-custom',
25940             cn: [
25941                 h,
25942                 {
25943                     tag: 'div',
25944                     cls: 'tab-content no-padding',
25945                     cn: []
25946                 }
25947             ]
25948         };
25949
25950         return  cfg;
25951     },
25952     initEvents : function()
25953     {
25954         //Roo.log('add add pane handler');
25955         this.on('addpane', this.onAddPane, this);
25956     },
25957      /**
25958      * Updates the box title
25959      * @param {String} html to set the title to.
25960      */
25961     setTitle : function(value)
25962     {
25963         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25964     },
25965     onAddPane : function(pane)
25966     {
25967         this.panes.push(pane);
25968         //Roo.log('addpane');
25969         //Roo.log(pane);
25970         // tabs are rendere left to right..
25971         if(!this.showtabs){
25972             return;
25973         }
25974         
25975         var ctr = this.el.select('.nav-tabs', true).first();
25976          
25977          
25978         var existing = ctr.select('.nav-tab',true);
25979         var qty = existing.getCount();;
25980         
25981         
25982         var tab = ctr.createChild({
25983             tag : 'li',
25984             cls : 'nav-tab' + (qty ? '' : ' active'),
25985             cn : [
25986                 {
25987                     tag : 'a',
25988                     href:'#',
25989                     html : pane.title
25990                 }
25991             ]
25992         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25993         pane.tab = tab;
25994         
25995         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25996         if (!qty) {
25997             pane.el.addClass('active');
25998         }
25999         
26000                 
26001     },
26002     onTabClick : function(ev,un,ob,pane)
26003     {
26004         //Roo.log('tab - prev default');
26005         ev.preventDefault();
26006         
26007         
26008         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26009         pane.tab.addClass('active');
26010         //Roo.log(pane.title);
26011         this.getChildContainer().select('.tab-pane',true).removeClass('active');
26012         // technically we should have a deactivate event.. but maybe add later.
26013         // and it should not de-activate the selected tab...
26014         this.fireEvent('activatepane', pane);
26015         pane.el.addClass('active');
26016         pane.fireEvent('activate');
26017         
26018         
26019     },
26020     
26021     getActivePane : function()
26022     {
26023         var r = false;
26024         Roo.each(this.panes, function(p) {
26025             if(p.el.hasClass('active')){
26026                 r = p;
26027                 return false;
26028             }
26029             
26030             return;
26031         });
26032         
26033         return r;
26034     }
26035     
26036     
26037 });
26038
26039  
26040 /*
26041  * - LGPL
26042  *
26043  * Tab pane
26044  * 
26045  */
26046 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26047 /**
26048  * @class Roo.bootstrap.TabPane
26049  * @extends Roo.bootstrap.Component
26050  * Bootstrap TabPane class
26051  * @cfg {Boolean} active (false | true) Default false
26052  * @cfg {String} title title of panel
26053
26054  * 
26055  * @constructor
26056  * Create a new TabPane
26057  * @param {Object} config The config object
26058  */
26059
26060 Roo.bootstrap.dash.TabPane = function(config){
26061     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26062     
26063     this.addEvents({
26064         // raw events
26065         /**
26066          * @event activate
26067          * When a pane is activated
26068          * @param {Roo.bootstrap.dash.TabPane} pane
26069          */
26070         "activate" : true
26071          
26072     });
26073 };
26074
26075 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
26076     
26077     active : false,
26078     title : '',
26079     
26080     // the tabBox that this is attached to.
26081     tab : false,
26082      
26083     getAutoCreate : function() 
26084     {
26085         var cfg = {
26086             tag: 'div',
26087             cls: 'tab-pane'
26088         };
26089         
26090         if(this.active){
26091             cfg.cls += ' active';
26092         }
26093         
26094         return cfg;
26095     },
26096     initEvents  : function()
26097     {
26098         //Roo.log('trigger add pane handler');
26099         this.parent().fireEvent('addpane', this)
26100     },
26101     
26102      /**
26103      * Updates the tab title 
26104      * @param {String} html to set the title to.
26105      */
26106     setTitle: function(str)
26107     {
26108         if (!this.tab) {
26109             return;
26110         }
26111         this.title = str;
26112         this.tab.select('a', true).first().dom.innerHTML = str;
26113         
26114     }
26115     
26116     
26117     
26118 });
26119
26120  
26121
26122
26123  /*
26124  * - LGPL
26125  *
26126  * menu
26127  * 
26128  */
26129 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26130
26131 /**
26132  * @class Roo.bootstrap.menu.Menu
26133  * @extends Roo.bootstrap.Component
26134  * Bootstrap Menu class - container for Menu
26135  * @cfg {String} html Text of the menu
26136  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26137  * @cfg {String} icon Font awesome icon
26138  * @cfg {String} pos Menu align to (top | bottom) default bottom
26139  * 
26140  * 
26141  * @constructor
26142  * Create a new Menu
26143  * @param {Object} config The config object
26144  */
26145
26146
26147 Roo.bootstrap.menu.Menu = function(config){
26148     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26149     
26150     this.addEvents({
26151         /**
26152          * @event beforeshow
26153          * Fires before this menu is displayed
26154          * @param {Roo.bootstrap.menu.Menu} this
26155          */
26156         beforeshow : true,
26157         /**
26158          * @event beforehide
26159          * Fires before this menu is hidden
26160          * @param {Roo.bootstrap.menu.Menu} this
26161          */
26162         beforehide : true,
26163         /**
26164          * @event show
26165          * Fires after this menu is displayed
26166          * @param {Roo.bootstrap.menu.Menu} this
26167          */
26168         show : true,
26169         /**
26170          * @event hide
26171          * Fires after this menu is hidden
26172          * @param {Roo.bootstrap.menu.Menu} this
26173          */
26174         hide : true,
26175         /**
26176          * @event click
26177          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26178          * @param {Roo.bootstrap.menu.Menu} this
26179          * @param {Roo.EventObject} e
26180          */
26181         click : true
26182     });
26183     
26184 };
26185
26186 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26187     
26188     submenu : false,
26189     html : '',
26190     weight : 'default',
26191     icon : false,
26192     pos : 'bottom',
26193     
26194     
26195     getChildContainer : function() {
26196         if(this.isSubMenu){
26197             return this.el;
26198         }
26199         
26200         return this.el.select('ul.dropdown-menu', true).first();  
26201     },
26202     
26203     getAutoCreate : function()
26204     {
26205         var text = [
26206             {
26207                 tag : 'span',
26208                 cls : 'roo-menu-text',
26209                 html : this.html
26210             }
26211         ];
26212         
26213         if(this.icon){
26214             text.unshift({
26215                 tag : 'i',
26216                 cls : 'fa ' + this.icon
26217             })
26218         }
26219         
26220         
26221         var cfg = {
26222             tag : 'div',
26223             cls : 'btn-group',
26224             cn : [
26225                 {
26226                     tag : 'button',
26227                     cls : 'dropdown-button btn btn-' + this.weight,
26228                     cn : text
26229                 },
26230                 {
26231                     tag : 'button',
26232                     cls : 'dropdown-toggle btn btn-' + this.weight,
26233                     cn : [
26234                         {
26235                             tag : 'span',
26236                             cls : 'caret'
26237                         }
26238                     ]
26239                 },
26240                 {
26241                     tag : 'ul',
26242                     cls : 'dropdown-menu'
26243                 }
26244             ]
26245             
26246         };
26247         
26248         if(this.pos == 'top'){
26249             cfg.cls += ' dropup';
26250         }
26251         
26252         if(this.isSubMenu){
26253             cfg = {
26254                 tag : 'ul',
26255                 cls : 'dropdown-menu'
26256             }
26257         }
26258         
26259         return cfg;
26260     },
26261     
26262     onRender : function(ct, position)
26263     {
26264         this.isSubMenu = ct.hasClass('dropdown-submenu');
26265         
26266         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26267     },
26268     
26269     initEvents : function() 
26270     {
26271         if(this.isSubMenu){
26272             return;
26273         }
26274         
26275         this.hidden = true;
26276         
26277         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26278         this.triggerEl.on('click', this.onTriggerPress, this);
26279         
26280         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26281         this.buttonEl.on('click', this.onClick, this);
26282         
26283     },
26284     
26285     list : function()
26286     {
26287         if(this.isSubMenu){
26288             return this.el;
26289         }
26290         
26291         return this.el.select('ul.dropdown-menu', true).first();
26292     },
26293     
26294     onClick : function(e)
26295     {
26296         this.fireEvent("click", this, e);
26297     },
26298     
26299     onTriggerPress  : function(e)
26300     {   
26301         if (this.isVisible()) {
26302             this.hide();
26303         } else {
26304             this.show();
26305         }
26306     },
26307     
26308     isVisible : function(){
26309         return !this.hidden;
26310     },
26311     
26312     show : function()
26313     {
26314         this.fireEvent("beforeshow", this);
26315         
26316         this.hidden = false;
26317         this.el.addClass('open');
26318         
26319         Roo.get(document).on("mouseup", this.onMouseUp, this);
26320         
26321         this.fireEvent("show", this);
26322         
26323         
26324     },
26325     
26326     hide : function()
26327     {
26328         this.fireEvent("beforehide", this);
26329         
26330         this.hidden = true;
26331         this.el.removeClass('open');
26332         
26333         Roo.get(document).un("mouseup", this.onMouseUp);
26334         
26335         this.fireEvent("hide", this);
26336     },
26337     
26338     onMouseUp : function()
26339     {
26340         this.hide();
26341     }
26342     
26343 });
26344
26345  
26346  /*
26347  * - LGPL
26348  *
26349  * menu item
26350  * 
26351  */
26352 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26353
26354 /**
26355  * @class Roo.bootstrap.menu.Item
26356  * @extends Roo.bootstrap.Component
26357  * Bootstrap MenuItem class
26358  * @cfg {Boolean} submenu (true | false) default false
26359  * @cfg {String} html text of the item
26360  * @cfg {String} href the link
26361  * @cfg {Boolean} disable (true | false) default false
26362  * @cfg {Boolean} preventDefault (true | false) default true
26363  * @cfg {String} icon Font awesome icon
26364  * @cfg {String} pos Submenu align to (left | right) default right 
26365  * 
26366  * 
26367  * @constructor
26368  * Create a new Item
26369  * @param {Object} config The config object
26370  */
26371
26372
26373 Roo.bootstrap.menu.Item = function(config){
26374     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26375     this.addEvents({
26376         /**
26377          * @event mouseover
26378          * Fires when the mouse is hovering over this menu
26379          * @param {Roo.bootstrap.menu.Item} this
26380          * @param {Roo.EventObject} e
26381          */
26382         mouseover : true,
26383         /**
26384          * @event mouseout
26385          * Fires when the mouse exits this menu
26386          * @param {Roo.bootstrap.menu.Item} this
26387          * @param {Roo.EventObject} e
26388          */
26389         mouseout : true,
26390         // raw events
26391         /**
26392          * @event click
26393          * The raw click event for the entire grid.
26394          * @param {Roo.EventObject} e
26395          */
26396         click : true
26397     });
26398 };
26399
26400 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26401     
26402     submenu : false,
26403     href : '',
26404     html : '',
26405     preventDefault: true,
26406     disable : false,
26407     icon : false,
26408     pos : 'right',
26409     
26410     getAutoCreate : function()
26411     {
26412         var text = [
26413             {
26414                 tag : 'span',
26415                 cls : 'roo-menu-item-text',
26416                 html : this.html
26417             }
26418         ];
26419         
26420         if(this.icon){
26421             text.unshift({
26422                 tag : 'i',
26423                 cls : 'fa ' + this.icon
26424             })
26425         }
26426         
26427         var cfg = {
26428             tag : 'li',
26429             cn : [
26430                 {
26431                     tag : 'a',
26432                     href : this.href || '#',
26433                     cn : text
26434                 }
26435             ]
26436         };
26437         
26438         if(this.disable){
26439             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26440         }
26441         
26442         if(this.submenu){
26443             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26444             
26445             if(this.pos == 'left'){
26446                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26447             }
26448         }
26449         
26450         return cfg;
26451     },
26452     
26453     initEvents : function() 
26454     {
26455         this.el.on('mouseover', this.onMouseOver, this);
26456         this.el.on('mouseout', this.onMouseOut, this);
26457         
26458         this.el.select('a', true).first().on('click', this.onClick, this);
26459         
26460     },
26461     
26462     onClick : function(e)
26463     {
26464         if(this.preventDefault){
26465             e.preventDefault();
26466         }
26467         
26468         this.fireEvent("click", this, e);
26469     },
26470     
26471     onMouseOver : function(e)
26472     {
26473         if(this.submenu && this.pos == 'left'){
26474             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26475         }
26476         
26477         this.fireEvent("mouseover", this, e);
26478     },
26479     
26480     onMouseOut : function(e)
26481     {
26482         this.fireEvent("mouseout", this, e);
26483     }
26484 });
26485
26486  
26487
26488  /*
26489  * - LGPL
26490  *
26491  * menu separator
26492  * 
26493  */
26494 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26495
26496 /**
26497  * @class Roo.bootstrap.menu.Separator
26498  * @extends Roo.bootstrap.Component
26499  * Bootstrap Separator class
26500  * 
26501  * @constructor
26502  * Create a new Separator
26503  * @param {Object} config The config object
26504  */
26505
26506
26507 Roo.bootstrap.menu.Separator = function(config){
26508     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26509 };
26510
26511 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26512     
26513     getAutoCreate : function(){
26514         var cfg = {
26515             tag : 'li',
26516             cls: 'divider'
26517         };
26518         
26519         return cfg;
26520     }
26521    
26522 });
26523
26524  
26525
26526  /*
26527  * - LGPL
26528  *
26529  * Tooltip
26530  * 
26531  */
26532
26533 /**
26534  * @class Roo.bootstrap.Tooltip
26535  * Bootstrap Tooltip class
26536  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26537  * to determine which dom element triggers the tooltip.
26538  * 
26539  * It needs to add support for additional attributes like tooltip-position
26540  * 
26541  * @constructor
26542  * Create a new Toolti
26543  * @param {Object} config The config object
26544  */
26545
26546 Roo.bootstrap.Tooltip = function(config){
26547     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26548     
26549     this.alignment = Roo.bootstrap.Tooltip.alignment;
26550     
26551     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26552         this.alignment = config.alignment;
26553     }
26554     
26555 };
26556
26557 Roo.apply(Roo.bootstrap.Tooltip, {
26558     /**
26559      * @function init initialize tooltip monitoring.
26560      * @static
26561      */
26562     currentEl : false,
26563     currentTip : false,
26564     currentRegion : false,
26565     
26566     //  init : delay?
26567     
26568     init : function()
26569     {
26570         Roo.get(document).on('mouseover', this.enter ,this);
26571         Roo.get(document).on('mouseout', this.leave, this);
26572          
26573         
26574         this.currentTip = new Roo.bootstrap.Tooltip();
26575     },
26576     
26577     enter : function(ev)
26578     {
26579         var dom = ev.getTarget();
26580         
26581         //Roo.log(['enter',dom]);
26582         var el = Roo.fly(dom);
26583         if (this.currentEl) {
26584             //Roo.log(dom);
26585             //Roo.log(this.currentEl);
26586             //Roo.log(this.currentEl.contains(dom));
26587             if (this.currentEl == el) {
26588                 return;
26589             }
26590             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26591                 return;
26592             }
26593
26594         }
26595         
26596         if (this.currentTip.el) {
26597             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26598         }    
26599         //Roo.log(ev);
26600         
26601         if(!el || el.dom == document){
26602             return;
26603         }
26604         
26605         var bindEl = el;
26606         
26607         // you can not look for children, as if el is the body.. then everythign is the child..
26608         if (!el.attr('tooltip')) { //
26609             if (!el.select("[tooltip]").elements.length) {
26610                 return;
26611             }
26612             // is the mouse over this child...?
26613             bindEl = el.select("[tooltip]").first();
26614             var xy = ev.getXY();
26615             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26616                 //Roo.log("not in region.");
26617                 return;
26618             }
26619             //Roo.log("child element over..");
26620             
26621         }
26622         this.currentEl = bindEl;
26623         this.currentTip.bind(bindEl);
26624         this.currentRegion = Roo.lib.Region.getRegion(dom);
26625         this.currentTip.enter();
26626         
26627     },
26628     leave : function(ev)
26629     {
26630         var dom = ev.getTarget();
26631         //Roo.log(['leave',dom]);
26632         if (!this.currentEl) {
26633             return;
26634         }
26635         
26636         
26637         if (dom != this.currentEl.dom) {
26638             return;
26639         }
26640         var xy = ev.getXY();
26641         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26642             return;
26643         }
26644         // only activate leave if mouse cursor is outside... bounding box..
26645         
26646         
26647         
26648         
26649         if (this.currentTip) {
26650             this.currentTip.leave();
26651         }
26652         //Roo.log('clear currentEl');
26653         this.currentEl = false;
26654         
26655         
26656     },
26657     alignment : {
26658         'left' : ['r-l', [-2,0], 'right'],
26659         'right' : ['l-r', [2,0], 'left'],
26660         'bottom' : ['t-b', [0,2], 'top'],
26661         'top' : [ 'b-t', [0,-2], 'bottom']
26662     }
26663     
26664 });
26665
26666
26667 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26668     
26669     
26670     bindEl : false,
26671     
26672     delay : null, // can be { show : 300 , hide: 500}
26673     
26674     timeout : null,
26675     
26676     hoverState : null, //???
26677     
26678     placement : 'bottom', 
26679     
26680     alignment : false,
26681     
26682     getAutoCreate : function(){
26683     
26684         var cfg = {
26685            cls : 'tooltip',
26686            role : 'tooltip',
26687            cn : [
26688                 {
26689                     cls : 'tooltip-arrow'
26690                 },
26691                 {
26692                     cls : 'tooltip-inner'
26693                 }
26694            ]
26695         };
26696         
26697         return cfg;
26698     },
26699     bind : function(el)
26700     {
26701         this.bindEl = el;
26702     },
26703       
26704     
26705     enter : function () {
26706        
26707         if (this.timeout != null) {
26708             clearTimeout(this.timeout);
26709         }
26710         
26711         this.hoverState = 'in';
26712          //Roo.log("enter - show");
26713         if (!this.delay || !this.delay.show) {
26714             this.show();
26715             return;
26716         }
26717         var _t = this;
26718         this.timeout = setTimeout(function () {
26719             if (_t.hoverState == 'in') {
26720                 _t.show();
26721             }
26722         }, this.delay.show);
26723     },
26724     leave : function()
26725     {
26726         clearTimeout(this.timeout);
26727     
26728         this.hoverState = 'out';
26729          if (!this.delay || !this.delay.hide) {
26730             this.hide();
26731             return;
26732         }
26733        
26734         var _t = this;
26735         this.timeout = setTimeout(function () {
26736             //Roo.log("leave - timeout");
26737             
26738             if (_t.hoverState == 'out') {
26739                 _t.hide();
26740                 Roo.bootstrap.Tooltip.currentEl = false;
26741             }
26742         }, delay);
26743     },
26744     
26745     show : function (msg)
26746     {
26747         if (!this.el) {
26748             this.render(document.body);
26749         }
26750         // set content.
26751         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26752         
26753         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26754         
26755         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26756         
26757         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26758         
26759         var placement = typeof this.placement == 'function' ?
26760             this.placement.call(this, this.el, on_el) :
26761             this.placement;
26762             
26763         var autoToken = /\s?auto?\s?/i;
26764         var autoPlace = autoToken.test(placement);
26765         if (autoPlace) {
26766             placement = placement.replace(autoToken, '') || 'top';
26767         }
26768         
26769         //this.el.detach()
26770         //this.el.setXY([0,0]);
26771         this.el.show();
26772         //this.el.dom.style.display='block';
26773         
26774         //this.el.appendTo(on_el);
26775         
26776         var p = this.getPosition();
26777         var box = this.el.getBox();
26778         
26779         if (autoPlace) {
26780             // fixme..
26781         }
26782         
26783         var align = this.alignment[placement];
26784         
26785         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26786         
26787         if(placement == 'top' || placement == 'bottom'){
26788             if(xy[0] < 0){
26789                 placement = 'right';
26790             }
26791             
26792             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26793                 placement = 'left';
26794             }
26795             
26796             var scroll = Roo.select('body', true).first().getScroll();
26797             
26798             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26799                 placement = 'top';
26800             }
26801             
26802             align = this.alignment[placement];
26803         }
26804         
26805         this.el.alignTo(this.bindEl, align[0],align[1]);
26806         //var arrow = this.el.select('.arrow',true).first();
26807         //arrow.set(align[2], 
26808         
26809         this.el.addClass(placement);
26810         
26811         this.el.addClass('in fade');
26812         
26813         this.hoverState = null;
26814         
26815         if (this.el.hasClass('fade')) {
26816             // fade it?
26817         }
26818         
26819     },
26820     hide : function()
26821     {
26822          
26823         if (!this.el) {
26824             return;
26825         }
26826         //this.el.setXY([0,0]);
26827         this.el.removeClass('in');
26828         //this.el.hide();
26829         
26830     }
26831     
26832 });
26833  
26834
26835  /*
26836  * - LGPL
26837  *
26838  * Location Picker
26839  * 
26840  */
26841
26842 /**
26843  * @class Roo.bootstrap.LocationPicker
26844  * @extends Roo.bootstrap.Component
26845  * Bootstrap LocationPicker class
26846  * @cfg {Number} latitude Position when init default 0
26847  * @cfg {Number} longitude Position when init default 0
26848  * @cfg {Number} zoom default 15
26849  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26850  * @cfg {Boolean} mapTypeControl default false
26851  * @cfg {Boolean} disableDoubleClickZoom default false
26852  * @cfg {Boolean} scrollwheel default true
26853  * @cfg {Boolean} streetViewControl default false
26854  * @cfg {Number} radius default 0
26855  * @cfg {String} locationName
26856  * @cfg {Boolean} draggable default true
26857  * @cfg {Boolean} enableAutocomplete default false
26858  * @cfg {Boolean} enableReverseGeocode default true
26859  * @cfg {String} markerTitle
26860  * 
26861  * @constructor
26862  * Create a new LocationPicker
26863  * @param {Object} config The config object
26864  */
26865
26866
26867 Roo.bootstrap.LocationPicker = function(config){
26868     
26869     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26870     
26871     this.addEvents({
26872         /**
26873          * @event initial
26874          * Fires when the picker initialized.
26875          * @param {Roo.bootstrap.LocationPicker} this
26876          * @param {Google Location} location
26877          */
26878         initial : true,
26879         /**
26880          * @event positionchanged
26881          * Fires when the picker position changed.
26882          * @param {Roo.bootstrap.LocationPicker} this
26883          * @param {Google Location} location
26884          */
26885         positionchanged : true,
26886         /**
26887          * @event resize
26888          * Fires when the map resize.
26889          * @param {Roo.bootstrap.LocationPicker} this
26890          */
26891         resize : true,
26892         /**
26893          * @event show
26894          * Fires when the map show.
26895          * @param {Roo.bootstrap.LocationPicker} this
26896          */
26897         show : true,
26898         /**
26899          * @event hide
26900          * Fires when the map hide.
26901          * @param {Roo.bootstrap.LocationPicker} this
26902          */
26903         hide : true,
26904         /**
26905          * @event mapClick
26906          * Fires when click the map.
26907          * @param {Roo.bootstrap.LocationPicker} this
26908          * @param {Map event} e
26909          */
26910         mapClick : true,
26911         /**
26912          * @event mapRightClick
26913          * Fires when right click the map.
26914          * @param {Roo.bootstrap.LocationPicker} this
26915          * @param {Map event} e
26916          */
26917         mapRightClick : true,
26918         /**
26919          * @event markerClick
26920          * Fires when click the marker.
26921          * @param {Roo.bootstrap.LocationPicker} this
26922          * @param {Map event} e
26923          */
26924         markerClick : true,
26925         /**
26926          * @event markerRightClick
26927          * Fires when right click the marker.
26928          * @param {Roo.bootstrap.LocationPicker} this
26929          * @param {Map event} e
26930          */
26931         markerRightClick : true,
26932         /**
26933          * @event OverlayViewDraw
26934          * Fires when OverlayView Draw
26935          * @param {Roo.bootstrap.LocationPicker} this
26936          */
26937         OverlayViewDraw : true,
26938         /**
26939          * @event OverlayViewOnAdd
26940          * Fires when OverlayView Draw
26941          * @param {Roo.bootstrap.LocationPicker} this
26942          */
26943         OverlayViewOnAdd : true,
26944         /**
26945          * @event OverlayViewOnRemove
26946          * Fires when OverlayView Draw
26947          * @param {Roo.bootstrap.LocationPicker} this
26948          */
26949         OverlayViewOnRemove : true,
26950         /**
26951          * @event OverlayViewShow
26952          * Fires when OverlayView Draw
26953          * @param {Roo.bootstrap.LocationPicker} this
26954          * @param {Pixel} cpx
26955          */
26956         OverlayViewShow : true,
26957         /**
26958          * @event OverlayViewHide
26959          * Fires when OverlayView Draw
26960          * @param {Roo.bootstrap.LocationPicker} this
26961          */
26962         OverlayViewHide : true,
26963         /**
26964          * @event loadexception
26965          * Fires when load google lib failed.
26966          * @param {Roo.bootstrap.LocationPicker} this
26967          */
26968         loadexception : true
26969     });
26970         
26971 };
26972
26973 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26974     
26975     gMapContext: false,
26976     
26977     latitude: 0,
26978     longitude: 0,
26979     zoom: 15,
26980     mapTypeId: false,
26981     mapTypeControl: false,
26982     disableDoubleClickZoom: false,
26983     scrollwheel: true,
26984     streetViewControl: false,
26985     radius: 0,
26986     locationName: '',
26987     draggable: true,
26988     enableAutocomplete: false,
26989     enableReverseGeocode: true,
26990     markerTitle: '',
26991     
26992     getAutoCreate: function()
26993     {
26994
26995         var cfg = {
26996             tag: 'div',
26997             cls: 'roo-location-picker'
26998         };
26999         
27000         return cfg
27001     },
27002     
27003     initEvents: function(ct, position)
27004     {       
27005         if(!this.el.getWidth() || this.isApplied()){
27006             return;
27007         }
27008         
27009         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27010         
27011         this.initial();
27012     },
27013     
27014     initial: function()
27015     {
27016         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27017             this.fireEvent('loadexception', this);
27018             return;
27019         }
27020         
27021         if(!this.mapTypeId){
27022             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27023         }
27024         
27025         this.gMapContext = this.GMapContext();
27026         
27027         this.initOverlayView();
27028         
27029         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27030         
27031         var _this = this;
27032                 
27033         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27034             _this.setPosition(_this.gMapContext.marker.position);
27035         });
27036         
27037         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27038             _this.fireEvent('mapClick', this, event);
27039             
27040         });
27041
27042         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27043             _this.fireEvent('mapRightClick', this, event);
27044             
27045         });
27046         
27047         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27048             _this.fireEvent('markerClick', this, event);
27049             
27050         });
27051
27052         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27053             _this.fireEvent('markerRightClick', this, event);
27054             
27055         });
27056         
27057         this.setPosition(this.gMapContext.location);
27058         
27059         this.fireEvent('initial', this, this.gMapContext.location);
27060     },
27061     
27062     initOverlayView: function()
27063     {
27064         var _this = this;
27065         
27066         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27067             
27068             draw: function()
27069             {
27070                 _this.fireEvent('OverlayViewDraw', _this);
27071             },
27072             
27073             onAdd: function()
27074             {
27075                 _this.fireEvent('OverlayViewOnAdd', _this);
27076             },
27077             
27078             onRemove: function()
27079             {
27080                 _this.fireEvent('OverlayViewOnRemove', _this);
27081             },
27082             
27083             show: function(cpx)
27084             {
27085                 _this.fireEvent('OverlayViewShow', _this, cpx);
27086             },
27087             
27088             hide: function()
27089             {
27090                 _this.fireEvent('OverlayViewHide', _this);
27091             }
27092             
27093         });
27094     },
27095     
27096     fromLatLngToContainerPixel: function(event)
27097     {
27098         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27099     },
27100     
27101     isApplied: function() 
27102     {
27103         return this.getGmapContext() == false ? false : true;
27104     },
27105     
27106     getGmapContext: function() 
27107     {
27108         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27109     },
27110     
27111     GMapContext: function() 
27112     {
27113         var position = new google.maps.LatLng(this.latitude, this.longitude);
27114         
27115         var _map = new google.maps.Map(this.el.dom, {
27116             center: position,
27117             zoom: this.zoom,
27118             mapTypeId: this.mapTypeId,
27119             mapTypeControl: this.mapTypeControl,
27120             disableDoubleClickZoom: this.disableDoubleClickZoom,
27121             scrollwheel: this.scrollwheel,
27122             streetViewControl: this.streetViewControl,
27123             locationName: this.locationName,
27124             draggable: this.draggable,
27125             enableAutocomplete: this.enableAutocomplete,
27126             enableReverseGeocode: this.enableReverseGeocode
27127         });
27128         
27129         var _marker = new google.maps.Marker({
27130             position: position,
27131             map: _map,
27132             title: this.markerTitle,
27133             draggable: this.draggable
27134         });
27135         
27136         return {
27137             map: _map,
27138             marker: _marker,
27139             circle: null,
27140             location: position,
27141             radius: this.radius,
27142             locationName: this.locationName,
27143             addressComponents: {
27144                 formatted_address: null,
27145                 addressLine1: null,
27146                 addressLine2: null,
27147                 streetName: null,
27148                 streetNumber: null,
27149                 city: null,
27150                 district: null,
27151                 state: null,
27152                 stateOrProvince: null
27153             },
27154             settings: this,
27155             domContainer: this.el.dom,
27156             geodecoder: new google.maps.Geocoder()
27157         };
27158     },
27159     
27160     drawCircle: function(center, radius, options) 
27161     {
27162         if (this.gMapContext.circle != null) {
27163             this.gMapContext.circle.setMap(null);
27164         }
27165         if (radius > 0) {
27166             radius *= 1;
27167             options = Roo.apply({}, options, {
27168                 strokeColor: "#0000FF",
27169                 strokeOpacity: .35,
27170                 strokeWeight: 2,
27171                 fillColor: "#0000FF",
27172                 fillOpacity: .2
27173             });
27174             
27175             options.map = this.gMapContext.map;
27176             options.radius = radius;
27177             options.center = center;
27178             this.gMapContext.circle = new google.maps.Circle(options);
27179             return this.gMapContext.circle;
27180         }
27181         
27182         return null;
27183     },
27184     
27185     setPosition: function(location) 
27186     {
27187         this.gMapContext.location = location;
27188         this.gMapContext.marker.setPosition(location);
27189         this.gMapContext.map.panTo(location);
27190         this.drawCircle(location, this.gMapContext.radius, {});
27191         
27192         var _this = this;
27193         
27194         if (this.gMapContext.settings.enableReverseGeocode) {
27195             this.gMapContext.geodecoder.geocode({
27196                 latLng: this.gMapContext.location
27197             }, function(results, status) {
27198                 
27199                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27200                     _this.gMapContext.locationName = results[0].formatted_address;
27201                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27202                     
27203                     _this.fireEvent('positionchanged', this, location);
27204                 }
27205             });
27206             
27207             return;
27208         }
27209         
27210         this.fireEvent('positionchanged', this, location);
27211     },
27212     
27213     resize: function()
27214     {
27215         google.maps.event.trigger(this.gMapContext.map, "resize");
27216         
27217         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27218         
27219         this.fireEvent('resize', this);
27220     },
27221     
27222     setPositionByLatLng: function(latitude, longitude)
27223     {
27224         this.setPosition(new google.maps.LatLng(latitude, longitude));
27225     },
27226     
27227     getCurrentPosition: function() 
27228     {
27229         return {
27230             latitude: this.gMapContext.location.lat(),
27231             longitude: this.gMapContext.location.lng()
27232         };
27233     },
27234     
27235     getAddressName: function() 
27236     {
27237         return this.gMapContext.locationName;
27238     },
27239     
27240     getAddressComponents: function() 
27241     {
27242         return this.gMapContext.addressComponents;
27243     },
27244     
27245     address_component_from_google_geocode: function(address_components) 
27246     {
27247         var result = {};
27248         
27249         for (var i = 0; i < address_components.length; i++) {
27250             var component = address_components[i];
27251             if (component.types.indexOf("postal_code") >= 0) {
27252                 result.postalCode = component.short_name;
27253             } else if (component.types.indexOf("street_number") >= 0) {
27254                 result.streetNumber = component.short_name;
27255             } else if (component.types.indexOf("route") >= 0) {
27256                 result.streetName = component.short_name;
27257             } else if (component.types.indexOf("neighborhood") >= 0) {
27258                 result.city = component.short_name;
27259             } else if (component.types.indexOf("locality") >= 0) {
27260                 result.city = component.short_name;
27261             } else if (component.types.indexOf("sublocality") >= 0) {
27262                 result.district = component.short_name;
27263             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27264                 result.stateOrProvince = component.short_name;
27265             } else if (component.types.indexOf("country") >= 0) {
27266                 result.country = component.short_name;
27267             }
27268         }
27269         
27270         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27271         result.addressLine2 = "";
27272         return result;
27273     },
27274     
27275     setZoomLevel: function(zoom)
27276     {
27277         this.gMapContext.map.setZoom(zoom);
27278     },
27279     
27280     show: function()
27281     {
27282         if(!this.el){
27283             return;
27284         }
27285         
27286         this.el.show();
27287         
27288         this.resize();
27289         
27290         this.fireEvent('show', this);
27291     },
27292     
27293     hide: function()
27294     {
27295         if(!this.el){
27296             return;
27297         }
27298         
27299         this.el.hide();
27300         
27301         this.fireEvent('hide', this);
27302     }
27303     
27304 });
27305
27306 Roo.apply(Roo.bootstrap.LocationPicker, {
27307     
27308     OverlayView : function(map, options)
27309     {
27310         options = options || {};
27311         
27312         this.setMap(map);
27313     }
27314     
27315     
27316 });/**
27317  * @class Roo.bootstrap.Alert
27318  * @extends Roo.bootstrap.Component
27319  * Bootstrap Alert class - shows an alert area box
27320  * eg
27321  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27322   Enter a valid email address
27323 </div>
27324  * @licence LGPL
27325  * @cfg {String} title The title of alert
27326  * @cfg {String} html The content of alert
27327  * @cfg {String} weight (  success | info | warning | danger )
27328  * @cfg {String} faicon font-awesomeicon
27329  * 
27330  * @constructor
27331  * Create a new alert
27332  * @param {Object} config The config object
27333  */
27334
27335
27336 Roo.bootstrap.Alert = function(config){
27337     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27338     
27339 };
27340
27341 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27342     
27343     title: '',
27344     html: '',
27345     weight: false,
27346     faicon: false,
27347     
27348     getAutoCreate : function()
27349     {
27350         
27351         var cfg = {
27352             tag : 'div',
27353             cls : 'alert',
27354             cn : [
27355                 {
27356                     tag : 'i',
27357                     cls : 'roo-alert-icon'
27358                     
27359                 },
27360                 {
27361                     tag : 'b',
27362                     cls : 'roo-alert-title',
27363                     html : this.title
27364                 },
27365                 {
27366                     tag : 'span',
27367                     cls : 'roo-alert-text',
27368                     html : this.html
27369                 }
27370             ]
27371         };
27372         
27373         if(this.faicon){
27374             cfg.cn[0].cls += ' fa ' + this.faicon;
27375         }
27376         
27377         if(this.weight){
27378             cfg.cls += ' alert-' + this.weight;
27379         }
27380         
27381         return cfg;
27382     },
27383     
27384     initEvents: function() 
27385     {
27386         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27387     },
27388     
27389     setTitle : function(str)
27390     {
27391         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27392     },
27393     
27394     setText : function(str)
27395     {
27396         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27397     },
27398     
27399     setWeight : function(weight)
27400     {
27401         if(this.weight){
27402             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27403         }
27404         
27405         this.weight = weight;
27406         
27407         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27408     },
27409     
27410     setIcon : function(icon)
27411     {
27412         if(this.faicon){
27413             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27414         }
27415         
27416         this.faicon = icon;
27417         
27418         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27419     },
27420     
27421     hide: function() 
27422     {
27423         this.el.hide();   
27424     },
27425     
27426     show: function() 
27427     {  
27428         this.el.show();   
27429     }
27430     
27431 });
27432
27433  
27434 /*
27435 * Licence: LGPL
27436 */
27437
27438 /**
27439  * @class Roo.bootstrap.UploadCropbox
27440  * @extends Roo.bootstrap.Component
27441  * Bootstrap UploadCropbox class
27442  * @cfg {String} emptyText show when image has been loaded
27443  * @cfg {String} rotateNotify show when image too small to rotate
27444  * @cfg {Number} errorTimeout default 3000
27445  * @cfg {Number} minWidth default 300
27446  * @cfg {Number} minHeight default 300
27447  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27448  * @cfg {Boolean} isDocument (true|false) default false
27449  * @cfg {String} url action url
27450  * @cfg {String} paramName default 'imageUpload'
27451  * @cfg {String} method default POST
27452  * @cfg {Boolean} loadMask (true|false) default true
27453  * @cfg {Boolean} loadingText default 'Loading...'
27454  * 
27455  * @constructor
27456  * Create a new UploadCropbox
27457  * @param {Object} config The config object
27458  */
27459
27460 Roo.bootstrap.UploadCropbox = function(config){
27461     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27462     
27463     this.addEvents({
27464         /**
27465          * @event beforeselectfile
27466          * Fire before select file
27467          * @param {Roo.bootstrap.UploadCropbox} this
27468          */
27469         "beforeselectfile" : true,
27470         /**
27471          * @event initial
27472          * Fire after initEvent
27473          * @param {Roo.bootstrap.UploadCropbox} this
27474          */
27475         "initial" : true,
27476         /**
27477          * @event crop
27478          * Fire after initEvent
27479          * @param {Roo.bootstrap.UploadCropbox} this
27480          * @param {String} data
27481          */
27482         "crop" : true,
27483         /**
27484          * @event prepare
27485          * Fire when preparing the file data
27486          * @param {Roo.bootstrap.UploadCropbox} this
27487          * @param {Object} file
27488          */
27489         "prepare" : true,
27490         /**
27491          * @event exception
27492          * Fire when get exception
27493          * @param {Roo.bootstrap.UploadCropbox} this
27494          * @param {XMLHttpRequest} xhr
27495          */
27496         "exception" : true,
27497         /**
27498          * @event beforeloadcanvas
27499          * Fire before load the canvas
27500          * @param {Roo.bootstrap.UploadCropbox} this
27501          * @param {String} src
27502          */
27503         "beforeloadcanvas" : true,
27504         /**
27505          * @event trash
27506          * Fire when trash image
27507          * @param {Roo.bootstrap.UploadCropbox} this
27508          */
27509         "trash" : true,
27510         /**
27511          * @event download
27512          * Fire when download the image
27513          * @param {Roo.bootstrap.UploadCropbox} this
27514          */
27515         "download" : true,
27516         /**
27517          * @event footerbuttonclick
27518          * Fire when footerbuttonclick
27519          * @param {Roo.bootstrap.UploadCropbox} this
27520          * @param {String} type
27521          */
27522         "footerbuttonclick" : true,
27523         /**
27524          * @event resize
27525          * Fire when resize
27526          * @param {Roo.bootstrap.UploadCropbox} this
27527          */
27528         "resize" : true,
27529         /**
27530          * @event rotate
27531          * Fire when rotate the image
27532          * @param {Roo.bootstrap.UploadCropbox} this
27533          * @param {String} pos
27534          */
27535         "rotate" : true,
27536         /**
27537          * @event inspect
27538          * Fire when inspect the file
27539          * @param {Roo.bootstrap.UploadCropbox} this
27540          * @param {Object} file
27541          */
27542         "inspect" : true,
27543         /**
27544          * @event upload
27545          * Fire when xhr upload the file
27546          * @param {Roo.bootstrap.UploadCropbox} this
27547          * @param {Object} data
27548          */
27549         "upload" : true,
27550         /**
27551          * @event arrange
27552          * Fire when arrange the file data
27553          * @param {Roo.bootstrap.UploadCropbox} this
27554          * @param {Object} formData
27555          */
27556         "arrange" : true
27557     });
27558     
27559     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27560 };
27561
27562 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27563     
27564     emptyText : 'Click to upload image',
27565     rotateNotify : 'Image is too small to rotate',
27566     errorTimeout : 3000,
27567     scale : 0,
27568     baseScale : 1,
27569     rotate : 0,
27570     dragable : false,
27571     pinching : false,
27572     mouseX : 0,
27573     mouseY : 0,
27574     cropData : false,
27575     minWidth : 300,
27576     minHeight : 300,
27577     file : false,
27578     exif : {},
27579     baseRotate : 1,
27580     cropType : 'image/jpeg',
27581     buttons : false,
27582     canvasLoaded : false,
27583     isDocument : false,
27584     method : 'POST',
27585     paramName : 'imageUpload',
27586     loadMask : true,
27587     loadingText : 'Loading...',
27588     maskEl : false,
27589     
27590     getAutoCreate : function()
27591     {
27592         var cfg = {
27593             tag : 'div',
27594             cls : 'roo-upload-cropbox',
27595             cn : [
27596                 {
27597                     tag : 'input',
27598                     cls : 'roo-upload-cropbox-selector',
27599                     type : 'file'
27600                 },
27601                 {
27602                     tag : 'div',
27603                     cls : 'roo-upload-cropbox-body',
27604                     style : 'cursor:pointer',
27605                     cn : [
27606                         {
27607                             tag : 'div',
27608                             cls : 'roo-upload-cropbox-preview'
27609                         },
27610                         {
27611                             tag : 'div',
27612                             cls : 'roo-upload-cropbox-thumb'
27613                         },
27614                         {
27615                             tag : 'div',
27616                             cls : 'roo-upload-cropbox-empty-notify',
27617                             html : this.emptyText
27618                         },
27619                         {
27620                             tag : 'div',
27621                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27622                             html : this.rotateNotify
27623                         }
27624                     ]
27625                 },
27626                 {
27627                     tag : 'div',
27628                     cls : 'roo-upload-cropbox-footer',
27629                     cn : {
27630                         tag : 'div',
27631                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27632                         cn : []
27633                     }
27634                 }
27635             ]
27636         };
27637         
27638         return cfg;
27639     },
27640     
27641     onRender : function(ct, position)
27642     {
27643         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27644         
27645         if (this.buttons.length) {
27646             
27647             Roo.each(this.buttons, function(bb) {
27648                 
27649                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27650                 
27651                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27652                 
27653             }, this);
27654         }
27655         
27656         if(this.loadMask){
27657             this.maskEl = this.el;
27658         }
27659     },
27660     
27661     initEvents : function()
27662     {
27663         this.urlAPI = (window.createObjectURL && window) || 
27664                                 (window.URL && URL.revokeObjectURL && URL) || 
27665                                 (window.webkitURL && webkitURL);
27666                         
27667         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27668         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27669         
27670         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27671         this.selectorEl.hide();
27672         
27673         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27674         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27675         
27676         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27677         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27678         this.thumbEl.hide();
27679         
27680         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27681         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27682         
27683         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27684         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27685         this.errorEl.hide();
27686         
27687         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27688         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27689         this.footerEl.hide();
27690         
27691         this.setThumbBoxSize();
27692         
27693         this.bind();
27694         
27695         this.resize();
27696         
27697         this.fireEvent('initial', this);
27698     },
27699
27700     bind : function()
27701     {
27702         var _this = this;
27703         
27704         window.addEventListener("resize", function() { _this.resize(); } );
27705         
27706         this.bodyEl.on('click', this.beforeSelectFile, this);
27707         
27708         if(Roo.isTouch){
27709             this.bodyEl.on('touchstart', this.onTouchStart, this);
27710             this.bodyEl.on('touchmove', this.onTouchMove, this);
27711             this.bodyEl.on('touchend', this.onTouchEnd, this);
27712         }
27713         
27714         if(!Roo.isTouch){
27715             this.bodyEl.on('mousedown', this.onMouseDown, this);
27716             this.bodyEl.on('mousemove', this.onMouseMove, this);
27717             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27718             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27719             Roo.get(document).on('mouseup', this.onMouseUp, this);
27720         }
27721         
27722         this.selectorEl.on('change', this.onFileSelected, this);
27723     },
27724     
27725     reset : function()
27726     {    
27727         this.scale = 0;
27728         this.baseScale = 1;
27729         this.rotate = 0;
27730         this.baseRotate = 1;
27731         this.dragable = false;
27732         this.pinching = false;
27733         this.mouseX = 0;
27734         this.mouseY = 0;
27735         this.cropData = false;
27736         this.notifyEl.dom.innerHTML = this.emptyText;
27737         
27738         this.selectorEl.dom.value = '';
27739         
27740     },
27741     
27742     resize : function()
27743     {
27744         if(this.fireEvent('resize', this) != false){
27745             this.setThumbBoxPosition();
27746             this.setCanvasPosition();
27747         }
27748     },
27749     
27750     onFooterButtonClick : function(e, el, o, type)
27751     {
27752         switch (type) {
27753             case 'rotate-left' :
27754                 this.onRotateLeft(e);
27755                 break;
27756             case 'rotate-right' :
27757                 this.onRotateRight(e);
27758                 break;
27759             case 'picture' :
27760                 this.beforeSelectFile(e);
27761                 break;
27762             case 'trash' :
27763                 this.trash(e);
27764                 break;
27765             case 'crop' :
27766                 this.crop(e);
27767                 break;
27768             case 'download' :
27769                 this.download(e);
27770                 break;
27771             default :
27772                 break;
27773         }
27774         
27775         this.fireEvent('footerbuttonclick', this, type);
27776     },
27777     
27778     beforeSelectFile : function(e)
27779     {
27780         e.preventDefault();
27781         
27782         if(this.fireEvent('beforeselectfile', this) != false){
27783             this.selectorEl.dom.click();
27784         }
27785     },
27786     
27787     onFileSelected : function(e)
27788     {
27789         e.preventDefault();
27790         
27791         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27792             return;
27793         }
27794         
27795         var file = this.selectorEl.dom.files[0];
27796         
27797         if(this.fireEvent('inspect', this, file) != false){
27798             this.prepare(file);
27799         }
27800         
27801     },
27802     
27803     trash : function(e)
27804     {
27805         this.fireEvent('trash', this);
27806     },
27807     
27808     download : function(e)
27809     {
27810         this.fireEvent('download', this);
27811     },
27812     
27813     loadCanvas : function(src)
27814     {   
27815         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27816             
27817             this.reset();
27818             
27819             this.imageEl = document.createElement('img');
27820             
27821             var _this = this;
27822             
27823             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27824             
27825             this.imageEl.src = src;
27826         }
27827     },
27828     
27829     onLoadCanvas : function()
27830     {   
27831         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27832         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27833         
27834         this.bodyEl.un('click', this.beforeSelectFile, this);
27835         
27836         this.notifyEl.hide();
27837         this.thumbEl.show();
27838         this.footerEl.show();
27839         
27840         this.baseRotateLevel();
27841         
27842         if(this.isDocument){
27843             this.setThumbBoxSize();
27844         }
27845         
27846         this.setThumbBoxPosition();
27847         
27848         this.baseScaleLevel();
27849         
27850         this.draw();
27851         
27852         this.resize();
27853         
27854         this.canvasLoaded = true;
27855         
27856         if(this.loadMask){
27857             this.maskEl.unmask();
27858         }
27859         
27860     },
27861     
27862     setCanvasPosition : function()
27863     {   
27864         if(!this.canvasEl){
27865             return;
27866         }
27867         
27868         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27869         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27870         
27871         this.previewEl.setLeft(pw);
27872         this.previewEl.setTop(ph);
27873         
27874     },
27875     
27876     onMouseDown : function(e)
27877     {   
27878         e.stopEvent();
27879         
27880         this.dragable = true;
27881         this.pinching = false;
27882         
27883         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27884             this.dragable = false;
27885             return;
27886         }
27887         
27888         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27889         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27890         
27891     },
27892     
27893     onMouseMove : function(e)
27894     {   
27895         e.stopEvent();
27896         
27897         if(!this.canvasLoaded){
27898             return;
27899         }
27900         
27901         if (!this.dragable){
27902             return;
27903         }
27904         
27905         var minX = Math.ceil(this.thumbEl.getLeft(true));
27906         var minY = Math.ceil(this.thumbEl.getTop(true));
27907         
27908         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27909         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27910         
27911         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27912         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27913         
27914         x = x - this.mouseX;
27915         y = y - this.mouseY;
27916         
27917         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27918         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27919         
27920         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27921         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27922         
27923         this.previewEl.setLeft(bgX);
27924         this.previewEl.setTop(bgY);
27925         
27926         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27927         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27928     },
27929     
27930     onMouseUp : function(e)
27931     {   
27932         e.stopEvent();
27933         
27934         this.dragable = false;
27935     },
27936     
27937     onMouseWheel : function(e)
27938     {   
27939         e.stopEvent();
27940         
27941         this.startScale = this.scale;
27942         
27943         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27944         
27945         if(!this.zoomable()){
27946             this.scale = this.startScale;
27947             return;
27948         }
27949         
27950         this.draw();
27951         
27952         return;
27953     },
27954     
27955     zoomable : function()
27956     {
27957         var minScale = this.thumbEl.getWidth() / this.minWidth;
27958         
27959         if(this.minWidth < this.minHeight){
27960             minScale = this.thumbEl.getHeight() / this.minHeight;
27961         }
27962         
27963         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27964         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27965         
27966         if(
27967                 this.isDocument &&
27968                 (this.rotate == 0 || this.rotate == 180) && 
27969                 (
27970                     width > this.imageEl.OriginWidth || 
27971                     height > this.imageEl.OriginHeight ||
27972                     (width < this.minWidth && height < this.minHeight)
27973                 )
27974         ){
27975             return false;
27976         }
27977         
27978         if(
27979                 this.isDocument &&
27980                 (this.rotate == 90 || this.rotate == 270) && 
27981                 (
27982                     width > this.imageEl.OriginWidth || 
27983                     height > this.imageEl.OriginHeight ||
27984                     (width < this.minHeight && height < this.minWidth)
27985                 )
27986         ){
27987             return false;
27988         }
27989         
27990         if(
27991                 !this.isDocument &&
27992                 (this.rotate == 0 || this.rotate == 180) && 
27993                 (
27994                     width < this.minWidth || 
27995                     width > this.imageEl.OriginWidth || 
27996                     height < this.minHeight || 
27997                     height > this.imageEl.OriginHeight
27998                 )
27999         ){
28000             return false;
28001         }
28002         
28003         if(
28004                 !this.isDocument &&
28005                 (this.rotate == 90 || this.rotate == 270) && 
28006                 (
28007                     width < this.minHeight || 
28008                     width > this.imageEl.OriginWidth || 
28009                     height < this.minWidth || 
28010                     height > this.imageEl.OriginHeight
28011                 )
28012         ){
28013             return false;
28014         }
28015         
28016         return true;
28017         
28018     },
28019     
28020     onRotateLeft : function(e)
28021     {   
28022         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28023             
28024             var minScale = this.thumbEl.getWidth() / this.minWidth;
28025             
28026             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28027             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28028             
28029             this.startScale = this.scale;
28030             
28031             while (this.getScaleLevel() < minScale){
28032             
28033                 this.scale = this.scale + 1;
28034                 
28035                 if(!this.zoomable()){
28036                     break;
28037                 }
28038                 
28039                 if(
28040                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28041                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28042                 ){
28043                     continue;
28044                 }
28045                 
28046                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28047
28048                 this.draw();
28049                 
28050                 return;
28051             }
28052             
28053             this.scale = this.startScale;
28054             
28055             this.onRotateFail();
28056             
28057             return false;
28058         }
28059         
28060         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28061
28062         if(this.isDocument){
28063             this.setThumbBoxSize();
28064             this.setThumbBoxPosition();
28065             this.setCanvasPosition();
28066         }
28067         
28068         this.draw();
28069         
28070         this.fireEvent('rotate', this, 'left');
28071         
28072     },
28073     
28074     onRotateRight : function(e)
28075     {
28076         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28077             
28078             var minScale = this.thumbEl.getWidth() / this.minWidth;
28079         
28080             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28081             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28082             
28083             this.startScale = this.scale;
28084             
28085             while (this.getScaleLevel() < minScale){
28086             
28087                 this.scale = this.scale + 1;
28088                 
28089                 if(!this.zoomable()){
28090                     break;
28091                 }
28092                 
28093                 if(
28094                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28095                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28096                 ){
28097                     continue;
28098                 }
28099                 
28100                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28101
28102                 this.draw();
28103                 
28104                 return;
28105             }
28106             
28107             this.scale = this.startScale;
28108             
28109             this.onRotateFail();
28110             
28111             return false;
28112         }
28113         
28114         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28115
28116         if(this.isDocument){
28117             this.setThumbBoxSize();
28118             this.setThumbBoxPosition();
28119             this.setCanvasPosition();
28120         }
28121         
28122         this.draw();
28123         
28124         this.fireEvent('rotate', this, 'right');
28125     },
28126     
28127     onRotateFail : function()
28128     {
28129         this.errorEl.show(true);
28130         
28131         var _this = this;
28132         
28133         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28134     },
28135     
28136     draw : function()
28137     {
28138         this.previewEl.dom.innerHTML = '';
28139         
28140         var canvasEl = document.createElement("canvas");
28141         
28142         var contextEl = canvasEl.getContext("2d");
28143         
28144         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28145         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28146         var center = this.imageEl.OriginWidth / 2;
28147         
28148         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28149             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28150             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28151             center = this.imageEl.OriginHeight / 2;
28152         }
28153         
28154         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28155         
28156         contextEl.translate(center, center);
28157         contextEl.rotate(this.rotate * Math.PI / 180);
28158
28159         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28160         
28161         this.canvasEl = document.createElement("canvas");
28162         
28163         this.contextEl = this.canvasEl.getContext("2d");
28164         
28165         switch (this.rotate) {
28166             case 0 :
28167                 
28168                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28169                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28170                 
28171                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28172                 
28173                 break;
28174             case 90 : 
28175                 
28176                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28177                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28178                 
28179                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28180                     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);
28181                     break;
28182                 }
28183                 
28184                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28185                 
28186                 break;
28187             case 180 :
28188                 
28189                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28190                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28191                 
28192                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28193                     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);
28194                     break;
28195                 }
28196                 
28197                 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);
28198                 
28199                 break;
28200             case 270 :
28201                 
28202                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28203                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28204         
28205                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28206                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28207                     break;
28208                 }
28209                 
28210                 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);
28211                 
28212                 break;
28213             default : 
28214                 break;
28215         }
28216         
28217         this.previewEl.appendChild(this.canvasEl);
28218         
28219         this.setCanvasPosition();
28220     },
28221     
28222     crop : function()
28223     {
28224         if(!this.canvasLoaded){
28225             return;
28226         }
28227         
28228         var imageCanvas = document.createElement("canvas");
28229         
28230         var imageContext = imageCanvas.getContext("2d");
28231         
28232         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28233         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28234         
28235         var center = imageCanvas.width / 2;
28236         
28237         imageContext.translate(center, center);
28238         
28239         imageContext.rotate(this.rotate * Math.PI / 180);
28240         
28241         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28242         
28243         var canvas = document.createElement("canvas");
28244         
28245         var context = canvas.getContext("2d");
28246                 
28247         canvas.width = this.minWidth;
28248         canvas.height = this.minHeight;
28249
28250         switch (this.rotate) {
28251             case 0 :
28252                 
28253                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28254                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28255                 
28256                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28257                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28258                 
28259                 var targetWidth = this.minWidth - 2 * x;
28260                 var targetHeight = this.minHeight - 2 * y;
28261                 
28262                 var scale = 1;
28263                 
28264                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28265                     scale = targetWidth / width;
28266                 }
28267                 
28268                 if(x > 0 && y == 0){
28269                     scale = targetHeight / height;
28270                 }
28271                 
28272                 if(x > 0 && y > 0){
28273                     scale = targetWidth / width;
28274                     
28275                     if(width < height){
28276                         scale = targetHeight / height;
28277                     }
28278                 }
28279                 
28280                 context.scale(scale, scale);
28281                 
28282                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28283                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28284
28285                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28286                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28287
28288                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28289                 
28290                 break;
28291             case 90 : 
28292                 
28293                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28294                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28295                 
28296                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28297                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28298                 
28299                 var targetWidth = this.minWidth - 2 * x;
28300                 var targetHeight = this.minHeight - 2 * y;
28301                 
28302                 var scale = 1;
28303                 
28304                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28305                     scale = targetWidth / width;
28306                 }
28307                 
28308                 if(x > 0 && y == 0){
28309                     scale = targetHeight / height;
28310                 }
28311                 
28312                 if(x > 0 && y > 0){
28313                     scale = targetWidth / width;
28314                     
28315                     if(width < height){
28316                         scale = targetHeight / height;
28317                     }
28318                 }
28319                 
28320                 context.scale(scale, scale);
28321                 
28322                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28323                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28324
28325                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28326                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28327                 
28328                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28329                 
28330                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28331                 
28332                 break;
28333             case 180 :
28334                 
28335                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28336                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28337                 
28338                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28339                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28340                 
28341                 var targetWidth = this.minWidth - 2 * x;
28342                 var targetHeight = this.minHeight - 2 * y;
28343                 
28344                 var scale = 1;
28345                 
28346                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28347                     scale = targetWidth / width;
28348                 }
28349                 
28350                 if(x > 0 && y == 0){
28351                     scale = targetHeight / height;
28352                 }
28353                 
28354                 if(x > 0 && y > 0){
28355                     scale = targetWidth / width;
28356                     
28357                     if(width < height){
28358                         scale = targetHeight / height;
28359                     }
28360                 }
28361                 
28362                 context.scale(scale, scale);
28363                 
28364                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28365                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28366
28367                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28368                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28369
28370                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28371                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28372                 
28373                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28374                 
28375                 break;
28376             case 270 :
28377                 
28378                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28379                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28380                 
28381                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28382                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28383                 
28384                 var targetWidth = this.minWidth - 2 * x;
28385                 var targetHeight = this.minHeight - 2 * y;
28386                 
28387                 var scale = 1;
28388                 
28389                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28390                     scale = targetWidth / width;
28391                 }
28392                 
28393                 if(x > 0 && y == 0){
28394                     scale = targetHeight / height;
28395                 }
28396                 
28397                 if(x > 0 && y > 0){
28398                     scale = targetWidth / width;
28399                     
28400                     if(width < height){
28401                         scale = targetHeight / height;
28402                     }
28403                 }
28404                 
28405                 context.scale(scale, scale);
28406                 
28407                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28408                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28409
28410                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28411                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28412                 
28413                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28414                 
28415                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28416                 
28417                 break;
28418             default : 
28419                 break;
28420         }
28421         
28422         this.cropData = canvas.toDataURL(this.cropType);
28423         
28424         if(this.fireEvent('crop', this, this.cropData) !== false){
28425             this.process(this.file, this.cropData);
28426         }
28427         
28428         return;
28429         
28430     },
28431     
28432     setThumbBoxSize : function()
28433     {
28434         var width, height;
28435         
28436         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28437             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28438             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28439             
28440             this.minWidth = width;
28441             this.minHeight = height;
28442             
28443             if(this.rotate == 90 || this.rotate == 270){
28444                 this.minWidth = height;
28445                 this.minHeight = width;
28446             }
28447         }
28448         
28449         height = 300;
28450         width = Math.ceil(this.minWidth * height / this.minHeight);
28451         
28452         if(this.minWidth > this.minHeight){
28453             width = 300;
28454             height = Math.ceil(this.minHeight * width / this.minWidth);
28455         }
28456         
28457         this.thumbEl.setStyle({
28458             width : width + 'px',
28459             height : height + 'px'
28460         });
28461
28462         return;
28463             
28464     },
28465     
28466     setThumbBoxPosition : function()
28467     {
28468         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28469         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28470         
28471         this.thumbEl.setLeft(x);
28472         this.thumbEl.setTop(y);
28473         
28474     },
28475     
28476     baseRotateLevel : function()
28477     {
28478         this.baseRotate = 1;
28479         
28480         if(
28481                 typeof(this.exif) != 'undefined' &&
28482                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28483                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28484         ){
28485             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28486         }
28487         
28488         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28489         
28490     },
28491     
28492     baseScaleLevel : function()
28493     {
28494         var width, height;
28495         
28496         if(this.isDocument){
28497             
28498             if(this.baseRotate == 6 || this.baseRotate == 8){
28499             
28500                 height = this.thumbEl.getHeight();
28501                 this.baseScale = height / this.imageEl.OriginWidth;
28502
28503                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28504                     width = this.thumbEl.getWidth();
28505                     this.baseScale = width / this.imageEl.OriginHeight;
28506                 }
28507
28508                 return;
28509             }
28510
28511             height = this.thumbEl.getHeight();
28512             this.baseScale = height / this.imageEl.OriginHeight;
28513
28514             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28515                 width = this.thumbEl.getWidth();
28516                 this.baseScale = width / this.imageEl.OriginWidth;
28517             }
28518
28519             return;
28520         }
28521         
28522         if(this.baseRotate == 6 || this.baseRotate == 8){
28523             
28524             width = this.thumbEl.getHeight();
28525             this.baseScale = width / this.imageEl.OriginHeight;
28526             
28527             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28528                 height = this.thumbEl.getWidth();
28529                 this.baseScale = height / this.imageEl.OriginHeight;
28530             }
28531             
28532             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28533                 height = this.thumbEl.getWidth();
28534                 this.baseScale = height / this.imageEl.OriginHeight;
28535                 
28536                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28537                     width = this.thumbEl.getHeight();
28538                     this.baseScale = width / this.imageEl.OriginWidth;
28539                 }
28540             }
28541             
28542             return;
28543         }
28544         
28545         width = this.thumbEl.getWidth();
28546         this.baseScale = width / this.imageEl.OriginWidth;
28547         
28548         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28549             height = this.thumbEl.getHeight();
28550             this.baseScale = height / this.imageEl.OriginHeight;
28551         }
28552         
28553         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28554             
28555             height = this.thumbEl.getHeight();
28556             this.baseScale = height / this.imageEl.OriginHeight;
28557             
28558             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28559                 width = this.thumbEl.getWidth();
28560                 this.baseScale = width / this.imageEl.OriginWidth;
28561             }
28562             
28563         }
28564         
28565         return;
28566     },
28567     
28568     getScaleLevel : function()
28569     {
28570         return this.baseScale * Math.pow(1.1, this.scale);
28571     },
28572     
28573     onTouchStart : function(e)
28574     {
28575         if(!this.canvasLoaded){
28576             this.beforeSelectFile(e);
28577             return;
28578         }
28579         
28580         var touches = e.browserEvent.touches;
28581         
28582         if(!touches){
28583             return;
28584         }
28585         
28586         if(touches.length == 1){
28587             this.onMouseDown(e);
28588             return;
28589         }
28590         
28591         if(touches.length != 2){
28592             return;
28593         }
28594         
28595         var coords = [];
28596         
28597         for(var i = 0, finger; finger = touches[i]; i++){
28598             coords.push(finger.pageX, finger.pageY);
28599         }
28600         
28601         var x = Math.pow(coords[0] - coords[2], 2);
28602         var y = Math.pow(coords[1] - coords[3], 2);
28603         
28604         this.startDistance = Math.sqrt(x + y);
28605         
28606         this.startScale = this.scale;
28607         
28608         this.pinching = true;
28609         this.dragable = false;
28610         
28611     },
28612     
28613     onTouchMove : function(e)
28614     {
28615         if(!this.pinching && !this.dragable){
28616             return;
28617         }
28618         
28619         var touches = e.browserEvent.touches;
28620         
28621         if(!touches){
28622             return;
28623         }
28624         
28625         if(this.dragable){
28626             this.onMouseMove(e);
28627             return;
28628         }
28629         
28630         var coords = [];
28631         
28632         for(var i = 0, finger; finger = touches[i]; i++){
28633             coords.push(finger.pageX, finger.pageY);
28634         }
28635         
28636         var x = Math.pow(coords[0] - coords[2], 2);
28637         var y = Math.pow(coords[1] - coords[3], 2);
28638         
28639         this.endDistance = Math.sqrt(x + y);
28640         
28641         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28642         
28643         if(!this.zoomable()){
28644             this.scale = this.startScale;
28645             return;
28646         }
28647         
28648         this.draw();
28649         
28650     },
28651     
28652     onTouchEnd : function(e)
28653     {
28654         this.pinching = false;
28655         this.dragable = false;
28656         
28657     },
28658     
28659     process : function(file, crop)
28660     {
28661         if(this.loadMask){
28662             this.maskEl.mask(this.loadingText);
28663         }
28664         
28665         this.xhr = new XMLHttpRequest();
28666         
28667         file.xhr = this.xhr;
28668
28669         this.xhr.open(this.method, this.url, true);
28670         
28671         var headers = {
28672             "Accept": "application/json",
28673             "Cache-Control": "no-cache",
28674             "X-Requested-With": "XMLHttpRequest"
28675         };
28676         
28677         for (var headerName in headers) {
28678             var headerValue = headers[headerName];
28679             if (headerValue) {
28680                 this.xhr.setRequestHeader(headerName, headerValue);
28681             }
28682         }
28683         
28684         var _this = this;
28685         
28686         this.xhr.onload = function()
28687         {
28688             _this.xhrOnLoad(_this.xhr);
28689         }
28690         
28691         this.xhr.onerror = function()
28692         {
28693             _this.xhrOnError(_this.xhr);
28694         }
28695         
28696         var formData = new FormData();
28697
28698         formData.append('returnHTML', 'NO');
28699         
28700         if(crop){
28701             formData.append('crop', crop);
28702         }
28703         
28704         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28705             formData.append(this.paramName, file, file.name);
28706         }
28707         
28708         if(typeof(file.filename) != 'undefined'){
28709             formData.append('filename', file.filename);
28710         }
28711         
28712         if(typeof(file.mimetype) != 'undefined'){
28713             formData.append('mimetype', file.mimetype);
28714         }
28715         
28716         if(this.fireEvent('arrange', this, formData) != false){
28717             this.xhr.send(formData);
28718         };
28719     },
28720     
28721     xhrOnLoad : function(xhr)
28722     {
28723         if(this.loadMask){
28724             this.maskEl.unmask();
28725         }
28726         
28727         if (xhr.readyState !== 4) {
28728             this.fireEvent('exception', this, xhr);
28729             return;
28730         }
28731
28732         var response = Roo.decode(xhr.responseText);
28733         
28734         if(!response.success){
28735             this.fireEvent('exception', this, xhr);
28736             return;
28737         }
28738         
28739         var response = Roo.decode(xhr.responseText);
28740         
28741         this.fireEvent('upload', this, response);
28742         
28743     },
28744     
28745     xhrOnError : function()
28746     {
28747         if(this.loadMask){
28748             this.maskEl.unmask();
28749         }
28750         
28751         Roo.log('xhr on error');
28752         
28753         var response = Roo.decode(xhr.responseText);
28754           
28755         Roo.log(response);
28756         
28757     },
28758     
28759     prepare : function(file)
28760     {   
28761         if(this.loadMask){
28762             this.maskEl.mask(this.loadingText);
28763         }
28764         
28765         this.file = false;
28766         this.exif = {};
28767         
28768         if(typeof(file) === 'string'){
28769             this.loadCanvas(file);
28770             return;
28771         }
28772         
28773         if(!file || !this.urlAPI){
28774             return;
28775         }
28776         
28777         this.file = file;
28778         this.cropType = file.type;
28779         
28780         var _this = this;
28781         
28782         if(this.fireEvent('prepare', this, this.file) != false){
28783             
28784             var reader = new FileReader();
28785             
28786             reader.onload = function (e) {
28787                 if (e.target.error) {
28788                     Roo.log(e.target.error);
28789                     return;
28790                 }
28791                 
28792                 var buffer = e.target.result,
28793                     dataView = new DataView(buffer),
28794                     offset = 2,
28795                     maxOffset = dataView.byteLength - 4,
28796                     markerBytes,
28797                     markerLength;
28798                 
28799                 if (dataView.getUint16(0) === 0xffd8) {
28800                     while (offset < maxOffset) {
28801                         markerBytes = dataView.getUint16(offset);
28802                         
28803                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28804                             markerLength = dataView.getUint16(offset + 2) + 2;
28805                             if (offset + markerLength > dataView.byteLength) {
28806                                 Roo.log('Invalid meta data: Invalid segment size.');
28807                                 break;
28808                             }
28809                             
28810                             if(markerBytes == 0xffe1){
28811                                 _this.parseExifData(
28812                                     dataView,
28813                                     offset,
28814                                     markerLength
28815                                 );
28816                             }
28817                             
28818                             offset += markerLength;
28819                             
28820                             continue;
28821                         }
28822                         
28823                         break;
28824                     }
28825                     
28826                 }
28827                 
28828                 var url = _this.urlAPI.createObjectURL(_this.file);
28829                 
28830                 _this.loadCanvas(url);
28831                 
28832                 return;
28833             }
28834             
28835             reader.readAsArrayBuffer(this.file);
28836             
28837         }
28838         
28839     },
28840     
28841     parseExifData : function(dataView, offset, length)
28842     {
28843         var tiffOffset = offset + 10,
28844             littleEndian,
28845             dirOffset;
28846     
28847         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28848             // No Exif data, might be XMP data instead
28849             return;
28850         }
28851         
28852         // Check for the ASCII code for "Exif" (0x45786966):
28853         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28854             // No Exif data, might be XMP data instead
28855             return;
28856         }
28857         if (tiffOffset + 8 > dataView.byteLength) {
28858             Roo.log('Invalid Exif data: Invalid segment size.');
28859             return;
28860         }
28861         // Check for the two null bytes:
28862         if (dataView.getUint16(offset + 8) !== 0x0000) {
28863             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28864             return;
28865         }
28866         // Check the byte alignment:
28867         switch (dataView.getUint16(tiffOffset)) {
28868         case 0x4949:
28869             littleEndian = true;
28870             break;
28871         case 0x4D4D:
28872             littleEndian = false;
28873             break;
28874         default:
28875             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28876             return;
28877         }
28878         // Check for the TIFF tag marker (0x002A):
28879         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28880             Roo.log('Invalid Exif data: Missing TIFF marker.');
28881             return;
28882         }
28883         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28884         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28885         
28886         this.parseExifTags(
28887             dataView,
28888             tiffOffset,
28889             tiffOffset + dirOffset,
28890             littleEndian
28891         );
28892     },
28893     
28894     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28895     {
28896         var tagsNumber,
28897             dirEndOffset,
28898             i;
28899         if (dirOffset + 6 > dataView.byteLength) {
28900             Roo.log('Invalid Exif data: Invalid directory offset.');
28901             return;
28902         }
28903         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28904         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28905         if (dirEndOffset + 4 > dataView.byteLength) {
28906             Roo.log('Invalid Exif data: Invalid directory size.');
28907             return;
28908         }
28909         for (i = 0; i < tagsNumber; i += 1) {
28910             this.parseExifTag(
28911                 dataView,
28912                 tiffOffset,
28913                 dirOffset + 2 + 12 * i, // tag offset
28914                 littleEndian
28915             );
28916         }
28917         // Return the offset to the next directory:
28918         return dataView.getUint32(dirEndOffset, littleEndian);
28919     },
28920     
28921     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28922     {
28923         var tag = dataView.getUint16(offset, littleEndian);
28924         
28925         this.exif[tag] = this.getExifValue(
28926             dataView,
28927             tiffOffset,
28928             offset,
28929             dataView.getUint16(offset + 2, littleEndian), // tag type
28930             dataView.getUint32(offset + 4, littleEndian), // tag length
28931             littleEndian
28932         );
28933     },
28934     
28935     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28936     {
28937         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28938             tagSize,
28939             dataOffset,
28940             values,
28941             i,
28942             str,
28943             c;
28944     
28945         if (!tagType) {
28946             Roo.log('Invalid Exif data: Invalid tag type.');
28947             return;
28948         }
28949         
28950         tagSize = tagType.size * length;
28951         // Determine if the value is contained in the dataOffset bytes,
28952         // or if the value at the dataOffset is a pointer to the actual data:
28953         dataOffset = tagSize > 4 ?
28954                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28955         if (dataOffset + tagSize > dataView.byteLength) {
28956             Roo.log('Invalid Exif data: Invalid data offset.');
28957             return;
28958         }
28959         if (length === 1) {
28960             return tagType.getValue(dataView, dataOffset, littleEndian);
28961         }
28962         values = [];
28963         for (i = 0; i < length; i += 1) {
28964             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28965         }
28966         
28967         if (tagType.ascii) {
28968             str = '';
28969             // Concatenate the chars:
28970             for (i = 0; i < values.length; i += 1) {
28971                 c = values[i];
28972                 // Ignore the terminating NULL byte(s):
28973                 if (c === '\u0000') {
28974                     break;
28975                 }
28976                 str += c;
28977             }
28978             return str;
28979         }
28980         return values;
28981     }
28982     
28983 });
28984
28985 Roo.apply(Roo.bootstrap.UploadCropbox, {
28986     tags : {
28987         'Orientation': 0x0112
28988     },
28989     
28990     Orientation: {
28991             1: 0, //'top-left',
28992 //            2: 'top-right',
28993             3: 180, //'bottom-right',
28994 //            4: 'bottom-left',
28995 //            5: 'left-top',
28996             6: 90, //'right-top',
28997 //            7: 'right-bottom',
28998             8: 270 //'left-bottom'
28999     },
29000     
29001     exifTagTypes : {
29002         // byte, 8-bit unsigned int:
29003         1: {
29004             getValue: function (dataView, dataOffset) {
29005                 return dataView.getUint8(dataOffset);
29006             },
29007             size: 1
29008         },
29009         // ascii, 8-bit byte:
29010         2: {
29011             getValue: function (dataView, dataOffset) {
29012                 return String.fromCharCode(dataView.getUint8(dataOffset));
29013             },
29014             size: 1,
29015             ascii: true
29016         },
29017         // short, 16 bit int:
29018         3: {
29019             getValue: function (dataView, dataOffset, littleEndian) {
29020                 return dataView.getUint16(dataOffset, littleEndian);
29021             },
29022             size: 2
29023         },
29024         // long, 32 bit int:
29025         4: {
29026             getValue: function (dataView, dataOffset, littleEndian) {
29027                 return dataView.getUint32(dataOffset, littleEndian);
29028             },
29029             size: 4
29030         },
29031         // rational = two long values, first is numerator, second is denominator:
29032         5: {
29033             getValue: function (dataView, dataOffset, littleEndian) {
29034                 return dataView.getUint32(dataOffset, littleEndian) /
29035                     dataView.getUint32(dataOffset + 4, littleEndian);
29036             },
29037             size: 8
29038         },
29039         // slong, 32 bit signed int:
29040         9: {
29041             getValue: function (dataView, dataOffset, littleEndian) {
29042                 return dataView.getInt32(dataOffset, littleEndian);
29043             },
29044             size: 4
29045         },
29046         // srational, two slongs, first is numerator, second is denominator:
29047         10: {
29048             getValue: function (dataView, dataOffset, littleEndian) {
29049                 return dataView.getInt32(dataOffset, littleEndian) /
29050                     dataView.getInt32(dataOffset + 4, littleEndian);
29051             },
29052             size: 8
29053         }
29054     },
29055     
29056     footer : {
29057         STANDARD : [
29058             {
29059                 tag : 'div',
29060                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29061                 action : 'rotate-left',
29062                 cn : [
29063                     {
29064                         tag : 'button',
29065                         cls : 'btn btn-default',
29066                         html : '<i class="fa fa-undo"></i>'
29067                     }
29068                 ]
29069             },
29070             {
29071                 tag : 'div',
29072                 cls : 'btn-group roo-upload-cropbox-picture',
29073                 action : 'picture',
29074                 cn : [
29075                     {
29076                         tag : 'button',
29077                         cls : 'btn btn-default',
29078                         html : '<i class="fa fa-picture-o"></i>'
29079                     }
29080                 ]
29081             },
29082             {
29083                 tag : 'div',
29084                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29085                 action : 'rotate-right',
29086                 cn : [
29087                     {
29088                         tag : 'button',
29089                         cls : 'btn btn-default',
29090                         html : '<i class="fa fa-repeat"></i>'
29091                     }
29092                 ]
29093             }
29094         ],
29095         DOCUMENT : [
29096             {
29097                 tag : 'div',
29098                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29099                 action : 'rotate-left',
29100                 cn : [
29101                     {
29102                         tag : 'button',
29103                         cls : 'btn btn-default',
29104                         html : '<i class="fa fa-undo"></i>'
29105                     }
29106                 ]
29107             },
29108             {
29109                 tag : 'div',
29110                 cls : 'btn-group roo-upload-cropbox-download',
29111                 action : 'download',
29112                 cn : [
29113                     {
29114                         tag : 'button',
29115                         cls : 'btn btn-default',
29116                         html : '<i class="fa fa-download"></i>'
29117                     }
29118                 ]
29119             },
29120             {
29121                 tag : 'div',
29122                 cls : 'btn-group roo-upload-cropbox-crop',
29123                 action : 'crop',
29124                 cn : [
29125                     {
29126                         tag : 'button',
29127                         cls : 'btn btn-default',
29128                         html : '<i class="fa fa-crop"></i>'
29129                     }
29130                 ]
29131             },
29132             {
29133                 tag : 'div',
29134                 cls : 'btn-group roo-upload-cropbox-trash',
29135                 action : 'trash',
29136                 cn : [
29137                     {
29138                         tag : 'button',
29139                         cls : 'btn btn-default',
29140                         html : '<i class="fa fa-trash"></i>'
29141                     }
29142                 ]
29143             },
29144             {
29145                 tag : 'div',
29146                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29147                 action : 'rotate-right',
29148                 cn : [
29149                     {
29150                         tag : 'button',
29151                         cls : 'btn btn-default',
29152                         html : '<i class="fa fa-repeat"></i>'
29153                     }
29154                 ]
29155             }
29156         ],
29157         ROTATOR : [
29158             {
29159                 tag : 'div',
29160                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29161                 action : 'rotate-left',
29162                 cn : [
29163                     {
29164                         tag : 'button',
29165                         cls : 'btn btn-default',
29166                         html : '<i class="fa fa-undo"></i>'
29167                     }
29168                 ]
29169             },
29170             {
29171                 tag : 'div',
29172                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29173                 action : 'rotate-right',
29174                 cn : [
29175                     {
29176                         tag : 'button',
29177                         cls : 'btn btn-default',
29178                         html : '<i class="fa fa-repeat"></i>'
29179                     }
29180                 ]
29181             }
29182         ]
29183     }
29184 });
29185
29186 /*
29187 * Licence: LGPL
29188 */
29189
29190 /**
29191  * @class Roo.bootstrap.DocumentManager
29192  * @extends Roo.bootstrap.Component
29193  * Bootstrap DocumentManager class
29194  * @cfg {String} paramName default 'imageUpload'
29195  * @cfg {String} toolTipName default 'filename'
29196  * @cfg {String} method default POST
29197  * @cfg {String} url action url
29198  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29199  * @cfg {Boolean} multiple multiple upload default true
29200  * @cfg {Number} thumbSize default 300
29201  * @cfg {String} fieldLabel
29202  * @cfg {Number} labelWidth default 4
29203  * @cfg {String} labelAlign (left|top) default left
29204  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29205 * @cfg {Number} labellg set the width of label (1-12)
29206  * @cfg {Number} labelmd set the width of label (1-12)
29207  * @cfg {Number} labelsm set the width of label (1-12)
29208  * @cfg {Number} labelxs set the width of label (1-12)
29209  * 
29210  * @constructor
29211  * Create a new DocumentManager
29212  * @param {Object} config The config object
29213  */
29214
29215 Roo.bootstrap.DocumentManager = function(config){
29216     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29217     
29218     this.files = [];
29219     this.delegates = [];
29220     
29221     this.addEvents({
29222         /**
29223          * @event initial
29224          * Fire when initial the DocumentManager
29225          * @param {Roo.bootstrap.DocumentManager} this
29226          */
29227         "initial" : true,
29228         /**
29229          * @event inspect
29230          * inspect selected file
29231          * @param {Roo.bootstrap.DocumentManager} this
29232          * @param {File} file
29233          */
29234         "inspect" : true,
29235         /**
29236          * @event exception
29237          * Fire when xhr load exception
29238          * @param {Roo.bootstrap.DocumentManager} this
29239          * @param {XMLHttpRequest} xhr
29240          */
29241         "exception" : true,
29242         /**
29243          * @event afterupload
29244          * Fire when xhr load exception
29245          * @param {Roo.bootstrap.DocumentManager} this
29246          * @param {XMLHttpRequest} xhr
29247          */
29248         "afterupload" : true,
29249         /**
29250          * @event prepare
29251          * prepare the form data
29252          * @param {Roo.bootstrap.DocumentManager} this
29253          * @param {Object} formData
29254          */
29255         "prepare" : true,
29256         /**
29257          * @event remove
29258          * Fire when remove the file
29259          * @param {Roo.bootstrap.DocumentManager} this
29260          * @param {Object} file
29261          */
29262         "remove" : true,
29263         /**
29264          * @event refresh
29265          * Fire after refresh the file
29266          * @param {Roo.bootstrap.DocumentManager} this
29267          */
29268         "refresh" : true,
29269         /**
29270          * @event click
29271          * Fire after click the image
29272          * @param {Roo.bootstrap.DocumentManager} this
29273          * @param {Object} file
29274          */
29275         "click" : true,
29276         /**
29277          * @event edit
29278          * Fire when upload a image and editable set to true
29279          * @param {Roo.bootstrap.DocumentManager} this
29280          * @param {Object} file
29281          */
29282         "edit" : true,
29283         /**
29284          * @event beforeselectfile
29285          * Fire before select file
29286          * @param {Roo.bootstrap.DocumentManager} this
29287          */
29288         "beforeselectfile" : true,
29289         /**
29290          * @event process
29291          * Fire before process file
29292          * @param {Roo.bootstrap.DocumentManager} this
29293          * @param {Object} file
29294          */
29295         "process" : true,
29296         /**
29297          * @event previewrendered
29298          * Fire when preview rendered
29299          * @param {Roo.bootstrap.DocumentManager} this
29300          * @param {Object} file
29301          */
29302         "previewrendered" : true,
29303         /**
29304          */
29305         "previewResize" : true
29306         
29307     });
29308 };
29309
29310 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29311     
29312     boxes : 0,
29313     inputName : '',
29314     thumbSize : 300,
29315     multiple : true,
29316     files : false,
29317     method : 'POST',
29318     url : '',
29319     paramName : 'imageUpload',
29320     toolTipName : 'filename',
29321     fieldLabel : '',
29322     labelWidth : 4,
29323     labelAlign : 'left',
29324     editable : true,
29325     delegates : false,
29326     xhr : false, 
29327     
29328     labellg : 0,
29329     labelmd : 0,
29330     labelsm : 0,
29331     labelxs : 0,
29332     
29333     getAutoCreate : function()
29334     {   
29335         var managerWidget = {
29336             tag : 'div',
29337             cls : 'roo-document-manager',
29338             cn : [
29339                 {
29340                     tag : 'input',
29341                     cls : 'roo-document-manager-selector',
29342                     type : 'file'
29343                 },
29344                 {
29345                     tag : 'div',
29346                     cls : 'roo-document-manager-uploader',
29347                     cn : [
29348                         {
29349                             tag : 'div',
29350                             cls : 'roo-document-manager-upload-btn',
29351                             html : '<i class="fa fa-plus"></i>'
29352                         }
29353                     ]
29354                     
29355                 }
29356             ]
29357         };
29358         
29359         var content = [
29360             {
29361                 tag : 'div',
29362                 cls : 'column col-md-12',
29363                 cn : managerWidget
29364             }
29365         ];
29366         
29367         if(this.fieldLabel.length){
29368             
29369             content = [
29370                 {
29371                     tag : 'div',
29372                     cls : 'column col-md-12',
29373                     html : this.fieldLabel
29374                 },
29375                 {
29376                     tag : 'div',
29377                     cls : 'column col-md-12',
29378                     cn : managerWidget
29379                 }
29380             ];
29381
29382             if(this.labelAlign == 'left'){
29383                 content = [
29384                     {
29385                         tag : 'div',
29386                         cls : 'column',
29387                         html : this.fieldLabel
29388                     },
29389                     {
29390                         tag : 'div',
29391                         cls : 'column',
29392                         cn : managerWidget
29393                     }
29394                 ];
29395                 
29396                 if(this.labelWidth > 12){
29397                     content[0].style = "width: " + this.labelWidth + 'px';
29398                 }
29399
29400                 if(this.labelWidth < 13 && this.labelmd == 0){
29401                     this.labelmd = this.labelWidth;
29402                 }
29403
29404                 if(this.labellg > 0){
29405                     content[0].cls += ' col-lg-' + this.labellg;
29406                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29407                 }
29408
29409                 if(this.labelmd > 0){
29410                     content[0].cls += ' col-md-' + this.labelmd;
29411                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29412                 }
29413
29414                 if(this.labelsm > 0){
29415                     content[0].cls += ' col-sm-' + this.labelsm;
29416                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29417                 }
29418
29419                 if(this.labelxs > 0){
29420                     content[0].cls += ' col-xs-' + this.labelxs;
29421                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29422                 }
29423                 
29424             }
29425         }
29426         
29427         var cfg = {
29428             tag : 'div',
29429             cls : 'row clearfix',
29430             cn : content
29431         };
29432         
29433         return cfg;
29434         
29435     },
29436     
29437     initEvents : function()
29438     {
29439         this.managerEl = this.el.select('.roo-document-manager', true).first();
29440         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29441         
29442         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29443         this.selectorEl.hide();
29444         
29445         if(this.multiple){
29446             this.selectorEl.attr('multiple', 'multiple');
29447         }
29448         
29449         this.selectorEl.on('change', this.onFileSelected, this);
29450         
29451         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29452         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29453         
29454         this.uploader.on('click', this.onUploaderClick, this);
29455         
29456         this.renderProgressDialog();
29457         
29458         var _this = this;
29459         
29460         window.addEventListener("resize", function() { _this.refresh(); } );
29461         
29462         this.fireEvent('initial', this);
29463     },
29464     
29465     renderProgressDialog : function()
29466     {
29467         var _this = this;
29468         
29469         this.progressDialog = new Roo.bootstrap.Modal({
29470             cls : 'roo-document-manager-progress-dialog',
29471             allow_close : false,
29472             animate : false,
29473             title : '',
29474             buttons : [
29475                 {
29476                     name  :'cancel',
29477                     weight : 'danger',
29478                     html : 'Cancel'
29479                 }
29480             ], 
29481             listeners : { 
29482                 btnclick : function() {
29483                     _this.uploadCancel();
29484                     this.hide();
29485                 }
29486             }
29487         });
29488          
29489         this.progressDialog.render(Roo.get(document.body));
29490          
29491         this.progress = new Roo.bootstrap.Progress({
29492             cls : 'roo-document-manager-progress',
29493             active : true,
29494             striped : true
29495         });
29496         
29497         this.progress.render(this.progressDialog.getChildContainer());
29498         
29499         this.progressBar = new Roo.bootstrap.ProgressBar({
29500             cls : 'roo-document-manager-progress-bar',
29501             aria_valuenow : 0,
29502             aria_valuemin : 0,
29503             aria_valuemax : 12,
29504             panel : 'success'
29505         });
29506         
29507         this.progressBar.render(this.progress.getChildContainer());
29508     },
29509     
29510     onUploaderClick : function(e)
29511     {
29512         e.preventDefault();
29513      
29514         if(this.fireEvent('beforeselectfile', this) != false){
29515             this.selectorEl.dom.click();
29516         }
29517         
29518     },
29519     
29520     onFileSelected : function(e)
29521     {
29522         e.preventDefault();
29523         
29524         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29525             return;
29526         }
29527         
29528         Roo.each(this.selectorEl.dom.files, function(file){
29529             if(this.fireEvent('inspect', this, file) != false){
29530                 this.files.push(file);
29531             }
29532         }, this);
29533         
29534         this.queue();
29535         
29536     },
29537     
29538     queue : function()
29539     {
29540         this.selectorEl.dom.value = '';
29541         
29542         if(!this.files || !this.files.length){
29543             return;
29544         }
29545         
29546         if(this.boxes > 0 && this.files.length > this.boxes){
29547             this.files = this.files.slice(0, this.boxes);
29548         }
29549         
29550         this.uploader.show();
29551         
29552         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29553             this.uploader.hide();
29554         }
29555         
29556         var _this = this;
29557         
29558         var files = [];
29559         
29560         var docs = [];
29561         
29562         Roo.each(this.files, function(file){
29563             
29564             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29565                 var f = this.renderPreview(file);
29566                 files.push(f);
29567                 return;
29568             }
29569             
29570             if(file.type.indexOf('image') != -1){
29571                 this.delegates.push(
29572                     (function(){
29573                         _this.process(file);
29574                     }).createDelegate(this)
29575                 );
29576         
29577                 return;
29578             }
29579             
29580             docs.push(
29581                 (function(){
29582                     _this.process(file);
29583                 }).createDelegate(this)
29584             );
29585             
29586         }, this);
29587         
29588         this.files = files;
29589         
29590         this.delegates = this.delegates.concat(docs);
29591         
29592         if(!this.delegates.length){
29593             this.refresh();
29594             return;
29595         }
29596         
29597         this.progressBar.aria_valuemax = this.delegates.length;
29598         
29599         this.arrange();
29600         
29601         return;
29602     },
29603     
29604     arrange : function()
29605     {
29606         if(!this.delegates.length){
29607             this.progressDialog.hide();
29608             this.refresh();
29609             return;
29610         }
29611         
29612         var delegate = this.delegates.shift();
29613         
29614         this.progressDialog.show();
29615         
29616         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29617         
29618         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29619         
29620         delegate();
29621     },
29622     
29623     refresh : function()
29624     {
29625         this.uploader.show();
29626         
29627         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29628             this.uploader.hide();
29629         }
29630         
29631         Roo.isTouch ? this.closable(false) : this.closable(true);
29632         
29633         this.fireEvent('refresh', this);
29634     },
29635     
29636     onRemove : function(e, el, o)
29637     {
29638         e.preventDefault();
29639         
29640         this.fireEvent('remove', this, o);
29641         
29642     },
29643     
29644     remove : function(o)
29645     {
29646         var files = [];
29647         
29648         Roo.each(this.files, function(file){
29649             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29650                 files.push(file);
29651                 return;
29652             }
29653
29654             o.target.remove();
29655
29656         }, this);
29657         
29658         this.files = files;
29659         
29660         this.refresh();
29661     },
29662     
29663     clear : function()
29664     {
29665         Roo.each(this.files, function(file){
29666             if(!file.target){
29667                 return;
29668             }
29669             
29670             file.target.remove();
29671
29672         }, this);
29673         
29674         this.files = [];
29675         
29676         this.refresh();
29677     },
29678     
29679     onClick : function(e, el, o)
29680     {
29681         e.preventDefault();
29682         
29683         this.fireEvent('click', this, o);
29684         
29685     },
29686     
29687     closable : function(closable)
29688     {
29689         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29690             
29691             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29692             
29693             if(closable){
29694                 el.show();
29695                 return;
29696             }
29697             
29698             el.hide();
29699             
29700         }, this);
29701     },
29702     
29703     xhrOnLoad : function(xhr)
29704     {
29705         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29706             el.remove();
29707         }, this);
29708         
29709         if (xhr.readyState !== 4) {
29710             this.arrange();
29711             this.fireEvent('exception', this, xhr);
29712             return;
29713         }
29714
29715         var response = Roo.decode(xhr.responseText);
29716         
29717         if(!response.success){
29718             this.arrange();
29719             this.fireEvent('exception', this, xhr);
29720             return;
29721         }
29722         
29723         var file = this.renderPreview(response.data);
29724         
29725         this.files.push(file);
29726         
29727         this.arrange();
29728         
29729         this.fireEvent('afterupload', this, xhr);
29730         
29731     },
29732     
29733     xhrOnError : function(xhr)
29734     {
29735         Roo.log('xhr on error');
29736         
29737         var response = Roo.decode(xhr.responseText);
29738           
29739         Roo.log(response);
29740         
29741         this.arrange();
29742     },
29743     
29744     process : function(file)
29745     {
29746         if(this.fireEvent('process', this, file) !== false){
29747             if(this.editable && file.type.indexOf('image') != -1){
29748                 this.fireEvent('edit', this, file);
29749                 return;
29750             }
29751
29752             this.uploadStart(file, false);
29753
29754             return;
29755         }
29756         
29757     },
29758     
29759     uploadStart : function(file, crop)
29760     {
29761         this.xhr = new XMLHttpRequest();
29762         
29763         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29764             this.arrange();
29765             return;
29766         }
29767         
29768         file.xhr = this.xhr;
29769             
29770         this.managerEl.createChild({
29771             tag : 'div',
29772             cls : 'roo-document-manager-loading',
29773             cn : [
29774                 {
29775                     tag : 'div',
29776                     tooltip : file.name,
29777                     cls : 'roo-document-manager-thumb',
29778                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29779                 }
29780             ]
29781
29782         });
29783
29784         this.xhr.open(this.method, this.url, true);
29785         
29786         var headers = {
29787             "Accept": "application/json",
29788             "Cache-Control": "no-cache",
29789             "X-Requested-With": "XMLHttpRequest"
29790         };
29791         
29792         for (var headerName in headers) {
29793             var headerValue = headers[headerName];
29794             if (headerValue) {
29795                 this.xhr.setRequestHeader(headerName, headerValue);
29796             }
29797         }
29798         
29799         var _this = this;
29800         
29801         this.xhr.onload = function()
29802         {
29803             _this.xhrOnLoad(_this.xhr);
29804         }
29805         
29806         this.xhr.onerror = function()
29807         {
29808             _this.xhrOnError(_this.xhr);
29809         }
29810         
29811         var formData = new FormData();
29812
29813         formData.append('returnHTML', 'NO');
29814         
29815         if(crop){
29816             formData.append('crop', crop);
29817         }
29818         
29819         formData.append(this.paramName, file, file.name);
29820         
29821         var options = {
29822             file : file, 
29823             manually : false
29824         };
29825         
29826         if(this.fireEvent('prepare', this, formData, options) != false){
29827             
29828             if(options.manually){
29829                 return;
29830             }
29831             
29832             this.xhr.send(formData);
29833             return;
29834         };
29835         
29836         this.uploadCancel();
29837     },
29838     
29839     uploadCancel : function()
29840     {
29841         if (this.xhr) {
29842             this.xhr.abort();
29843         }
29844         
29845         this.delegates = [];
29846         
29847         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29848             el.remove();
29849         }, this);
29850         
29851         this.arrange();
29852     },
29853     
29854     renderPreview : function(file)
29855     {
29856         if(typeof(file.target) != 'undefined' && file.target){
29857             return file;
29858         }
29859         
29860         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29861         
29862         var previewEl = this.managerEl.createChild({
29863             tag : 'div',
29864             cls : 'roo-document-manager-preview',
29865             cn : [
29866                 {
29867                     tag : 'div',
29868                     tooltip : file[this.toolTipName],
29869                     cls : 'roo-document-manager-thumb',
29870                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29871                 },
29872                 {
29873                     tag : 'button',
29874                     cls : 'close',
29875                     html : '<i class="fa fa-times-circle"></i>'
29876                 }
29877             ]
29878         });
29879
29880         var close = previewEl.select('button.close', true).first();
29881
29882         close.on('click', this.onRemove, this, file);
29883
29884         file.target = previewEl;
29885
29886         var image = previewEl.select('img', true).first();
29887         
29888         var _this = this;
29889         
29890         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29891         
29892         image.on('click', this.onClick, this, file);
29893         
29894         this.fireEvent('previewrendered', this, file);
29895         
29896         return file;
29897         
29898     },
29899     
29900     onPreviewLoad : function(file, image)
29901     {
29902         if(typeof(file.target) == 'undefined' || !file.target){
29903             return;
29904         }
29905         
29906         var width = image.dom.naturalWidth || image.dom.width;
29907         var height = image.dom.naturalHeight || image.dom.height;
29908         
29909         if(!this.previewResize) {
29910             return;
29911         }
29912         
29913         if(width > height){
29914             file.target.addClass('wide');
29915             return;
29916         }
29917         
29918         file.target.addClass('tall');
29919         return;
29920         
29921     },
29922     
29923     uploadFromSource : function(file, crop)
29924     {
29925         this.xhr = new XMLHttpRequest();
29926         
29927         this.managerEl.createChild({
29928             tag : 'div',
29929             cls : 'roo-document-manager-loading',
29930             cn : [
29931                 {
29932                     tag : 'div',
29933                     tooltip : file.name,
29934                     cls : 'roo-document-manager-thumb',
29935                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29936                 }
29937             ]
29938
29939         });
29940
29941         this.xhr.open(this.method, this.url, true);
29942         
29943         var headers = {
29944             "Accept": "application/json",
29945             "Cache-Control": "no-cache",
29946             "X-Requested-With": "XMLHttpRequest"
29947         };
29948         
29949         for (var headerName in headers) {
29950             var headerValue = headers[headerName];
29951             if (headerValue) {
29952                 this.xhr.setRequestHeader(headerName, headerValue);
29953             }
29954         }
29955         
29956         var _this = this;
29957         
29958         this.xhr.onload = function()
29959         {
29960             _this.xhrOnLoad(_this.xhr);
29961         }
29962         
29963         this.xhr.onerror = function()
29964         {
29965             _this.xhrOnError(_this.xhr);
29966         }
29967         
29968         var formData = new FormData();
29969
29970         formData.append('returnHTML', 'NO');
29971         
29972         formData.append('crop', crop);
29973         
29974         if(typeof(file.filename) != 'undefined'){
29975             formData.append('filename', file.filename);
29976         }
29977         
29978         if(typeof(file.mimetype) != 'undefined'){
29979             formData.append('mimetype', file.mimetype);
29980         }
29981         
29982         Roo.log(formData);
29983         
29984         if(this.fireEvent('prepare', this, formData) != false){
29985             this.xhr.send(formData);
29986         };
29987     }
29988 });
29989
29990 /*
29991 * Licence: LGPL
29992 */
29993
29994 /**
29995  * @class Roo.bootstrap.DocumentViewer
29996  * @extends Roo.bootstrap.Component
29997  * Bootstrap DocumentViewer class
29998  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29999  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
30000  * 
30001  * @constructor
30002  * Create a new DocumentViewer
30003  * @param {Object} config The config object
30004  */
30005
30006 Roo.bootstrap.DocumentViewer = function(config){
30007     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30008     
30009     this.addEvents({
30010         /**
30011          * @event initial
30012          * Fire after initEvent
30013          * @param {Roo.bootstrap.DocumentViewer} this
30014          */
30015         "initial" : true,
30016         /**
30017          * @event click
30018          * Fire after click
30019          * @param {Roo.bootstrap.DocumentViewer} this
30020          */
30021         "click" : true,
30022         /**
30023          * @event download
30024          * Fire after download button
30025          * @param {Roo.bootstrap.DocumentViewer} this
30026          */
30027         "download" : true,
30028         /**
30029          * @event trash
30030          * Fire after trash button
30031          * @param {Roo.bootstrap.DocumentViewer} this
30032          */
30033         "trash" : true
30034         
30035     });
30036 };
30037
30038 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
30039     
30040     showDownload : true,
30041     
30042     showTrash : true,
30043     
30044     getAutoCreate : function()
30045     {
30046         var cfg = {
30047             tag : 'div',
30048             cls : 'roo-document-viewer',
30049             cn : [
30050                 {
30051                     tag : 'div',
30052                     cls : 'roo-document-viewer-body',
30053                     cn : [
30054                         {
30055                             tag : 'div',
30056                             cls : 'roo-document-viewer-thumb',
30057                             cn : [
30058                                 {
30059                                     tag : 'img',
30060                                     cls : 'roo-document-viewer-image'
30061                                 }
30062                             ]
30063                         }
30064                     ]
30065                 },
30066                 {
30067                     tag : 'div',
30068                     cls : 'roo-document-viewer-footer',
30069                     cn : {
30070                         tag : 'div',
30071                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30072                         cn : [
30073                             {
30074                                 tag : 'div',
30075                                 cls : 'btn-group roo-document-viewer-download',
30076                                 cn : [
30077                                     {
30078                                         tag : 'button',
30079                                         cls : 'btn btn-default',
30080                                         html : '<i class="fa fa-download"></i>'
30081                                     }
30082                                 ]
30083                             },
30084                             {
30085                                 tag : 'div',
30086                                 cls : 'btn-group roo-document-viewer-trash',
30087                                 cn : [
30088                                     {
30089                                         tag : 'button',
30090                                         cls : 'btn btn-default',
30091                                         html : '<i class="fa fa-trash"></i>'
30092                                     }
30093                                 ]
30094                             }
30095                         ]
30096                     }
30097                 }
30098             ]
30099         };
30100         
30101         return cfg;
30102     },
30103     
30104     initEvents : function()
30105     {
30106         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30107         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30108         
30109         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30110         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30111         
30112         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30113         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30114         
30115         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30116         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30117         
30118         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30119         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30120         
30121         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30122         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30123         
30124         this.bodyEl.on('click', this.onClick, this);
30125         this.downloadBtn.on('click', this.onDownload, this);
30126         this.trashBtn.on('click', this.onTrash, this);
30127         
30128         this.downloadBtn.hide();
30129         this.trashBtn.hide();
30130         
30131         if(this.showDownload){
30132             this.downloadBtn.show();
30133         }
30134         
30135         if(this.showTrash){
30136             this.trashBtn.show();
30137         }
30138         
30139         if(!this.showDownload && !this.showTrash) {
30140             this.footerEl.hide();
30141         }
30142         
30143     },
30144     
30145     initial : function()
30146     {
30147         this.fireEvent('initial', this);
30148         
30149     },
30150     
30151     onClick : function(e)
30152     {
30153         e.preventDefault();
30154         
30155         this.fireEvent('click', this);
30156     },
30157     
30158     onDownload : function(e)
30159     {
30160         e.preventDefault();
30161         
30162         this.fireEvent('download', this);
30163     },
30164     
30165     onTrash : function(e)
30166     {
30167         e.preventDefault();
30168         
30169         this.fireEvent('trash', this);
30170     }
30171     
30172 });
30173 /*
30174  * - LGPL
30175  *
30176  * nav progress bar
30177  * 
30178  */
30179
30180 /**
30181  * @class Roo.bootstrap.NavProgressBar
30182  * @extends Roo.bootstrap.Component
30183  * Bootstrap NavProgressBar class
30184  * 
30185  * @constructor
30186  * Create a new nav progress bar
30187  * @param {Object} config The config object
30188  */
30189
30190 Roo.bootstrap.NavProgressBar = function(config){
30191     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30192
30193     this.bullets = this.bullets || [];
30194    
30195 //    Roo.bootstrap.NavProgressBar.register(this);
30196      this.addEvents({
30197         /**
30198              * @event changed
30199              * Fires when the active item changes
30200              * @param {Roo.bootstrap.NavProgressBar} this
30201              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30202              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30203          */
30204         'changed': true
30205      });
30206     
30207 };
30208
30209 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30210     
30211     bullets : [],
30212     barItems : [],
30213     
30214     getAutoCreate : function()
30215     {
30216         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30217         
30218         cfg = {
30219             tag : 'div',
30220             cls : 'roo-navigation-bar-group',
30221             cn : [
30222                 {
30223                     tag : 'div',
30224                     cls : 'roo-navigation-top-bar'
30225                 },
30226                 {
30227                     tag : 'div',
30228                     cls : 'roo-navigation-bullets-bar',
30229                     cn : [
30230                         {
30231                             tag : 'ul',
30232                             cls : 'roo-navigation-bar'
30233                         }
30234                     ]
30235                 },
30236                 
30237                 {
30238                     tag : 'div',
30239                     cls : 'roo-navigation-bottom-bar'
30240                 }
30241             ]
30242             
30243         };
30244         
30245         return cfg;
30246         
30247     },
30248     
30249     initEvents: function() 
30250     {
30251         
30252     },
30253     
30254     onRender : function(ct, position) 
30255     {
30256         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30257         
30258         if(this.bullets.length){
30259             Roo.each(this.bullets, function(b){
30260                this.addItem(b);
30261             }, this);
30262         }
30263         
30264         this.format();
30265         
30266     },
30267     
30268     addItem : function(cfg)
30269     {
30270         var item = new Roo.bootstrap.NavProgressItem(cfg);
30271         
30272         item.parentId = this.id;
30273         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30274         
30275         if(cfg.html){
30276             var top = new Roo.bootstrap.Element({
30277                 tag : 'div',
30278                 cls : 'roo-navigation-bar-text'
30279             });
30280             
30281             var bottom = new Roo.bootstrap.Element({
30282                 tag : 'div',
30283                 cls : 'roo-navigation-bar-text'
30284             });
30285             
30286             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30287             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30288             
30289             var topText = new Roo.bootstrap.Element({
30290                 tag : 'span',
30291                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30292             });
30293             
30294             var bottomText = new Roo.bootstrap.Element({
30295                 tag : 'span',
30296                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30297             });
30298             
30299             topText.onRender(top.el, null);
30300             bottomText.onRender(bottom.el, null);
30301             
30302             item.topEl = top;
30303             item.bottomEl = bottom;
30304         }
30305         
30306         this.barItems.push(item);
30307         
30308         return item;
30309     },
30310     
30311     getActive : function()
30312     {
30313         var active = false;
30314         
30315         Roo.each(this.barItems, function(v){
30316             
30317             if (!v.isActive()) {
30318                 return;
30319             }
30320             
30321             active = v;
30322             return false;
30323             
30324         });
30325         
30326         return active;
30327     },
30328     
30329     setActiveItem : function(item)
30330     {
30331         var prev = false;
30332         
30333         Roo.each(this.barItems, function(v){
30334             if (v.rid == item.rid) {
30335                 return ;
30336             }
30337             
30338             if (v.isActive()) {
30339                 v.setActive(false);
30340                 prev = v;
30341             }
30342         });
30343
30344         item.setActive(true);
30345         
30346         this.fireEvent('changed', this, item, prev);
30347     },
30348     
30349     getBarItem: function(rid)
30350     {
30351         var ret = false;
30352         
30353         Roo.each(this.barItems, function(e) {
30354             if (e.rid != rid) {
30355                 return;
30356             }
30357             
30358             ret =  e;
30359             return false;
30360         });
30361         
30362         return ret;
30363     },
30364     
30365     indexOfItem : function(item)
30366     {
30367         var index = false;
30368         
30369         Roo.each(this.barItems, function(v, i){
30370             
30371             if (v.rid != item.rid) {
30372                 return;
30373             }
30374             
30375             index = i;
30376             return false
30377         });
30378         
30379         return index;
30380     },
30381     
30382     setActiveNext : function()
30383     {
30384         var i = this.indexOfItem(this.getActive());
30385         
30386         if (i > this.barItems.length) {
30387             return;
30388         }
30389         
30390         this.setActiveItem(this.barItems[i+1]);
30391     },
30392     
30393     setActivePrev : function()
30394     {
30395         var i = this.indexOfItem(this.getActive());
30396         
30397         if (i  < 1) {
30398             return;
30399         }
30400         
30401         this.setActiveItem(this.barItems[i-1]);
30402     },
30403     
30404     format : function()
30405     {
30406         if(!this.barItems.length){
30407             return;
30408         }
30409      
30410         var width = 100 / this.barItems.length;
30411         
30412         Roo.each(this.barItems, function(i){
30413             i.el.setStyle('width', width + '%');
30414             i.topEl.el.setStyle('width', width + '%');
30415             i.bottomEl.el.setStyle('width', width + '%');
30416         }, this);
30417         
30418     }
30419     
30420 });
30421 /*
30422  * - LGPL
30423  *
30424  * Nav Progress Item
30425  * 
30426  */
30427
30428 /**
30429  * @class Roo.bootstrap.NavProgressItem
30430  * @extends Roo.bootstrap.Component
30431  * Bootstrap NavProgressItem class
30432  * @cfg {String} rid the reference id
30433  * @cfg {Boolean} active (true|false) Is item active default false
30434  * @cfg {Boolean} disabled (true|false) Is item active default false
30435  * @cfg {String} html
30436  * @cfg {String} position (top|bottom) text position default bottom
30437  * @cfg {String} icon show icon instead of number
30438  * 
30439  * @constructor
30440  * Create a new NavProgressItem
30441  * @param {Object} config The config object
30442  */
30443 Roo.bootstrap.NavProgressItem = function(config){
30444     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30445     this.addEvents({
30446         // raw events
30447         /**
30448          * @event click
30449          * The raw click event for the entire grid.
30450          * @param {Roo.bootstrap.NavProgressItem} this
30451          * @param {Roo.EventObject} e
30452          */
30453         "click" : true
30454     });
30455    
30456 };
30457
30458 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30459     
30460     rid : '',
30461     active : false,
30462     disabled : false,
30463     html : '',
30464     position : 'bottom',
30465     icon : false,
30466     
30467     getAutoCreate : function()
30468     {
30469         var iconCls = 'roo-navigation-bar-item-icon';
30470         
30471         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30472         
30473         var cfg = {
30474             tag: 'li',
30475             cls: 'roo-navigation-bar-item',
30476             cn : [
30477                 {
30478                     tag : 'i',
30479                     cls : iconCls
30480                 }
30481             ]
30482         };
30483         
30484         if(this.active){
30485             cfg.cls += ' active';
30486         }
30487         if(this.disabled){
30488             cfg.cls += ' disabled';
30489         }
30490         
30491         return cfg;
30492     },
30493     
30494     disable : function()
30495     {
30496         this.setDisabled(true);
30497     },
30498     
30499     enable : function()
30500     {
30501         this.setDisabled(false);
30502     },
30503     
30504     initEvents: function() 
30505     {
30506         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30507         
30508         this.iconEl.on('click', this.onClick, this);
30509     },
30510     
30511     onClick : function(e)
30512     {
30513         e.preventDefault();
30514         
30515         if(this.disabled){
30516             return;
30517         }
30518         
30519         if(this.fireEvent('click', this, e) === false){
30520             return;
30521         };
30522         
30523         this.parent().setActiveItem(this);
30524     },
30525     
30526     isActive: function () 
30527     {
30528         return this.active;
30529     },
30530     
30531     setActive : function(state)
30532     {
30533         if(this.active == state){
30534             return;
30535         }
30536         
30537         this.active = state;
30538         
30539         if (state) {
30540             this.el.addClass('active');
30541             return;
30542         }
30543         
30544         this.el.removeClass('active');
30545         
30546         return;
30547     },
30548     
30549     setDisabled : function(state)
30550     {
30551         if(this.disabled == state){
30552             return;
30553         }
30554         
30555         this.disabled = state;
30556         
30557         if (state) {
30558             this.el.addClass('disabled');
30559             return;
30560         }
30561         
30562         this.el.removeClass('disabled');
30563     },
30564     
30565     tooltipEl : function()
30566     {
30567         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30568     }
30569 });
30570  
30571
30572  /*
30573  * - LGPL
30574  *
30575  * FieldLabel
30576  * 
30577  */
30578
30579 /**
30580  * @class Roo.bootstrap.FieldLabel
30581  * @extends Roo.bootstrap.Component
30582  * Bootstrap FieldLabel class
30583  * @cfg {String} html contents of the element
30584  * @cfg {String} tag tag of the element default label
30585  * @cfg {String} cls class of the element
30586  * @cfg {String} target label target 
30587  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30588  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30589  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30590  * @cfg {String} iconTooltip default "This field is required"
30591  * @cfg {String} indicatorpos (left|right) default left
30592  * 
30593  * @constructor
30594  * Create a new FieldLabel
30595  * @param {Object} config The config object
30596  */
30597
30598 Roo.bootstrap.FieldLabel = function(config){
30599     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30600     
30601     this.addEvents({
30602             /**
30603              * @event invalid
30604              * Fires after the field has been marked as invalid.
30605              * @param {Roo.form.FieldLabel} this
30606              * @param {String} msg The validation message
30607              */
30608             invalid : true,
30609             /**
30610              * @event valid
30611              * Fires after the field has been validated with no errors.
30612              * @param {Roo.form.FieldLabel} this
30613              */
30614             valid : true
30615         });
30616 };
30617
30618 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30619     
30620     tag: 'label',
30621     cls: '',
30622     html: '',
30623     target: '',
30624     allowBlank : true,
30625     invalidClass : 'has-warning',
30626     validClass : 'has-success',
30627     iconTooltip : 'This field is required',
30628     indicatorpos : 'left',
30629     
30630     getAutoCreate : function(){
30631         
30632         var cls = "";
30633         if (!this.allowBlank) {
30634             cls  = "visible";
30635         }
30636         
30637         var cfg = {
30638             tag : this.tag,
30639             cls : 'roo-bootstrap-field-label ' + this.cls,
30640             for : this.target,
30641             cn : [
30642                 {
30643                     tag : 'i',
30644                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30645                     tooltip : this.iconTooltip
30646                 },
30647                 {
30648                     tag : 'span',
30649                     html : this.html
30650                 }
30651             ] 
30652         };
30653         
30654         if(this.indicatorpos == 'right'){
30655             var cfg = {
30656                 tag : this.tag,
30657                 cls : 'roo-bootstrap-field-label ' + this.cls,
30658                 for : this.target,
30659                 cn : [
30660                     {
30661                         tag : 'span',
30662                         html : this.html
30663                     },
30664                     {
30665                         tag : 'i',
30666                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30667                         tooltip : this.iconTooltip
30668                     }
30669                 ] 
30670             };
30671         }
30672         
30673         return cfg;
30674     },
30675     
30676     initEvents: function() 
30677     {
30678         Roo.bootstrap.Element.superclass.initEvents.call(this);
30679         
30680         this.indicator = this.indicatorEl();
30681         
30682         if(this.indicator){
30683             this.indicator.removeClass('visible');
30684             this.indicator.addClass('invisible');
30685         }
30686         
30687         Roo.bootstrap.FieldLabel.register(this);
30688     },
30689     
30690     indicatorEl : function()
30691     {
30692         var indicator = this.el.select('i.roo-required-indicator',true).first();
30693         
30694         if(!indicator){
30695             return false;
30696         }
30697         
30698         return indicator;
30699         
30700     },
30701     
30702     /**
30703      * Mark this field as valid
30704      */
30705     markValid : function()
30706     {
30707         if(this.indicator){
30708             this.indicator.removeClass('visible');
30709             this.indicator.addClass('invisible');
30710         }
30711         if (Roo.bootstrap.version == 3) {
30712             this.el.removeClass(this.invalidClass);
30713             this.el.addClass(this.validClass);
30714         } else {
30715             this.el.removeClass('is-invalid');
30716             this.el.addClass('is-valid');
30717         }
30718         
30719         
30720         this.fireEvent('valid', this);
30721     },
30722     
30723     /**
30724      * Mark this field as invalid
30725      * @param {String} msg The validation message
30726      */
30727     markInvalid : function(msg)
30728     {
30729         if(this.indicator){
30730             this.indicator.removeClass('invisible');
30731             this.indicator.addClass('visible');
30732         }
30733           if (Roo.bootstrap.version == 3) {
30734             this.el.removeClass(this.validClass);
30735             this.el.addClass(this.invalidClass);
30736         } else {
30737             this.el.removeClass('is-valid');
30738             this.el.addClass('is-invalid');
30739         }
30740         
30741         
30742         this.fireEvent('invalid', this, msg);
30743     }
30744     
30745    
30746 });
30747
30748 Roo.apply(Roo.bootstrap.FieldLabel, {
30749     
30750     groups: {},
30751     
30752      /**
30753     * register a FieldLabel Group
30754     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30755     */
30756     register : function(label)
30757     {
30758         if(this.groups.hasOwnProperty(label.target)){
30759             return;
30760         }
30761      
30762         this.groups[label.target] = label;
30763         
30764     },
30765     /**
30766     * fetch a FieldLabel Group based on the target
30767     * @param {string} target
30768     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30769     */
30770     get: function(target) {
30771         if (typeof(this.groups[target]) == 'undefined') {
30772             return false;
30773         }
30774         
30775         return this.groups[target] ;
30776     }
30777 });
30778
30779  
30780
30781  /*
30782  * - LGPL
30783  *
30784  * page DateSplitField.
30785  * 
30786  */
30787
30788
30789 /**
30790  * @class Roo.bootstrap.DateSplitField
30791  * @extends Roo.bootstrap.Component
30792  * Bootstrap DateSplitField class
30793  * @cfg {string} fieldLabel - the label associated
30794  * @cfg {Number} labelWidth set the width of label (0-12)
30795  * @cfg {String} labelAlign (top|left)
30796  * @cfg {Boolean} dayAllowBlank (true|false) default false
30797  * @cfg {Boolean} monthAllowBlank (true|false) default false
30798  * @cfg {Boolean} yearAllowBlank (true|false) default false
30799  * @cfg {string} dayPlaceholder 
30800  * @cfg {string} monthPlaceholder
30801  * @cfg {string} yearPlaceholder
30802  * @cfg {string} dayFormat default 'd'
30803  * @cfg {string} monthFormat default 'm'
30804  * @cfg {string} yearFormat default 'Y'
30805  * @cfg {Number} labellg set the width of label (1-12)
30806  * @cfg {Number} labelmd set the width of label (1-12)
30807  * @cfg {Number} labelsm set the width of label (1-12)
30808  * @cfg {Number} labelxs set the width of label (1-12)
30809
30810  *     
30811  * @constructor
30812  * Create a new DateSplitField
30813  * @param {Object} config The config object
30814  */
30815
30816 Roo.bootstrap.DateSplitField = function(config){
30817     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30818     
30819     this.addEvents({
30820         // raw events
30821          /**
30822          * @event years
30823          * getting the data of years
30824          * @param {Roo.bootstrap.DateSplitField} this
30825          * @param {Object} years
30826          */
30827         "years" : true,
30828         /**
30829          * @event days
30830          * getting the data of days
30831          * @param {Roo.bootstrap.DateSplitField} this
30832          * @param {Object} days
30833          */
30834         "days" : true,
30835         /**
30836          * @event invalid
30837          * Fires after the field has been marked as invalid.
30838          * @param {Roo.form.Field} this
30839          * @param {String} msg The validation message
30840          */
30841         invalid : true,
30842        /**
30843          * @event valid
30844          * Fires after the field has been validated with no errors.
30845          * @param {Roo.form.Field} this
30846          */
30847         valid : true
30848     });
30849 };
30850
30851 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30852     
30853     fieldLabel : '',
30854     labelAlign : 'top',
30855     labelWidth : 3,
30856     dayAllowBlank : false,
30857     monthAllowBlank : false,
30858     yearAllowBlank : false,
30859     dayPlaceholder : '',
30860     monthPlaceholder : '',
30861     yearPlaceholder : '',
30862     dayFormat : 'd',
30863     monthFormat : 'm',
30864     yearFormat : 'Y',
30865     isFormField : true,
30866     labellg : 0,
30867     labelmd : 0,
30868     labelsm : 0,
30869     labelxs : 0,
30870     
30871     getAutoCreate : function()
30872     {
30873         var cfg = {
30874             tag : 'div',
30875             cls : 'row roo-date-split-field-group',
30876             cn : [
30877                 {
30878                     tag : 'input',
30879                     type : 'hidden',
30880                     cls : 'form-hidden-field roo-date-split-field-group-value',
30881                     name : this.name
30882                 }
30883             ]
30884         };
30885         
30886         var labelCls = 'col-md-12';
30887         var contentCls = 'col-md-4';
30888         
30889         if(this.fieldLabel){
30890             
30891             var label = {
30892                 tag : 'div',
30893                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30894                 cn : [
30895                     {
30896                         tag : 'label',
30897                         html : this.fieldLabel
30898                     }
30899                 ]
30900             };
30901             
30902             if(this.labelAlign == 'left'){
30903             
30904                 if(this.labelWidth > 12){
30905                     label.style = "width: " + this.labelWidth + 'px';
30906                 }
30907
30908                 if(this.labelWidth < 13 && this.labelmd == 0){
30909                     this.labelmd = this.labelWidth;
30910                 }
30911
30912                 if(this.labellg > 0){
30913                     labelCls = ' col-lg-' + this.labellg;
30914                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30915                 }
30916
30917                 if(this.labelmd > 0){
30918                     labelCls = ' col-md-' + this.labelmd;
30919                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30920                 }
30921
30922                 if(this.labelsm > 0){
30923                     labelCls = ' col-sm-' + this.labelsm;
30924                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30925                 }
30926
30927                 if(this.labelxs > 0){
30928                     labelCls = ' col-xs-' + this.labelxs;
30929                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30930                 }
30931             }
30932             
30933             label.cls += ' ' + labelCls;
30934             
30935             cfg.cn.push(label);
30936         }
30937         
30938         Roo.each(['day', 'month', 'year'], function(t){
30939             cfg.cn.push({
30940                 tag : 'div',
30941                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30942             });
30943         }, this);
30944         
30945         return cfg;
30946     },
30947     
30948     inputEl: function ()
30949     {
30950         return this.el.select('.roo-date-split-field-group-value', true).first();
30951     },
30952     
30953     onRender : function(ct, position) 
30954     {
30955         var _this = this;
30956         
30957         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30958         
30959         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30960         
30961         this.dayField = new Roo.bootstrap.ComboBox({
30962             allowBlank : this.dayAllowBlank,
30963             alwaysQuery : true,
30964             displayField : 'value',
30965             editable : false,
30966             fieldLabel : '',
30967             forceSelection : true,
30968             mode : 'local',
30969             placeholder : this.dayPlaceholder,
30970             selectOnFocus : true,
30971             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30972             triggerAction : 'all',
30973             typeAhead : true,
30974             valueField : 'value',
30975             store : new Roo.data.SimpleStore({
30976                 data : (function() {    
30977                     var days = [];
30978                     _this.fireEvent('days', _this, days);
30979                     return days;
30980                 })(),
30981                 fields : [ 'value' ]
30982             }),
30983             listeners : {
30984                 select : function (_self, record, index)
30985                 {
30986                     _this.setValue(_this.getValue());
30987                 }
30988             }
30989         });
30990
30991         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30992         
30993         this.monthField = new Roo.bootstrap.MonthField({
30994             after : '<i class=\"fa fa-calendar\"></i>',
30995             allowBlank : this.monthAllowBlank,
30996             placeholder : this.monthPlaceholder,
30997             readOnly : true,
30998             listeners : {
30999                 render : function (_self)
31000                 {
31001                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
31002                         e.preventDefault();
31003                         _self.focus();
31004                     });
31005                 },
31006                 select : function (_self, oldvalue, newvalue)
31007                 {
31008                     _this.setValue(_this.getValue());
31009                 }
31010             }
31011         });
31012         
31013         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31014         
31015         this.yearField = new Roo.bootstrap.ComboBox({
31016             allowBlank : this.yearAllowBlank,
31017             alwaysQuery : true,
31018             displayField : 'value',
31019             editable : false,
31020             fieldLabel : '',
31021             forceSelection : true,
31022             mode : 'local',
31023             placeholder : this.yearPlaceholder,
31024             selectOnFocus : true,
31025             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31026             triggerAction : 'all',
31027             typeAhead : true,
31028             valueField : 'value',
31029             store : new Roo.data.SimpleStore({
31030                 data : (function() {
31031                     var years = [];
31032                     _this.fireEvent('years', _this, years);
31033                     return years;
31034                 })(),
31035                 fields : [ 'value' ]
31036             }),
31037             listeners : {
31038                 select : function (_self, record, index)
31039                 {
31040                     _this.setValue(_this.getValue());
31041                 }
31042             }
31043         });
31044
31045         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31046     },
31047     
31048     setValue : function(v, format)
31049     {
31050         this.inputEl.dom.value = v;
31051         
31052         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31053         
31054         var d = Date.parseDate(v, f);
31055         
31056         if(!d){
31057             this.validate();
31058             return;
31059         }
31060         
31061         this.setDay(d.format(this.dayFormat));
31062         this.setMonth(d.format(this.monthFormat));
31063         this.setYear(d.format(this.yearFormat));
31064         
31065         this.validate();
31066         
31067         return;
31068     },
31069     
31070     setDay : function(v)
31071     {
31072         this.dayField.setValue(v);
31073         this.inputEl.dom.value = this.getValue();
31074         this.validate();
31075         return;
31076     },
31077     
31078     setMonth : function(v)
31079     {
31080         this.monthField.setValue(v, true);
31081         this.inputEl.dom.value = this.getValue();
31082         this.validate();
31083         return;
31084     },
31085     
31086     setYear : function(v)
31087     {
31088         this.yearField.setValue(v);
31089         this.inputEl.dom.value = this.getValue();
31090         this.validate();
31091         return;
31092     },
31093     
31094     getDay : function()
31095     {
31096         return this.dayField.getValue();
31097     },
31098     
31099     getMonth : function()
31100     {
31101         return this.monthField.getValue();
31102     },
31103     
31104     getYear : function()
31105     {
31106         return this.yearField.getValue();
31107     },
31108     
31109     getValue : function()
31110     {
31111         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31112         
31113         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31114         
31115         return date;
31116     },
31117     
31118     reset : function()
31119     {
31120         this.setDay('');
31121         this.setMonth('');
31122         this.setYear('');
31123         this.inputEl.dom.value = '';
31124         this.validate();
31125         return;
31126     },
31127     
31128     validate : function()
31129     {
31130         var d = this.dayField.validate();
31131         var m = this.monthField.validate();
31132         var y = this.yearField.validate();
31133         
31134         var valid = true;
31135         
31136         if(
31137                 (!this.dayAllowBlank && !d) ||
31138                 (!this.monthAllowBlank && !m) ||
31139                 (!this.yearAllowBlank && !y)
31140         ){
31141             valid = false;
31142         }
31143         
31144         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31145             return valid;
31146         }
31147         
31148         if(valid){
31149             this.markValid();
31150             return valid;
31151         }
31152         
31153         this.markInvalid();
31154         
31155         return valid;
31156     },
31157     
31158     markValid : function()
31159     {
31160         
31161         var label = this.el.select('label', true).first();
31162         var icon = this.el.select('i.fa-star', true).first();
31163
31164         if(label && icon){
31165             icon.remove();
31166         }
31167         
31168         this.fireEvent('valid', this);
31169     },
31170     
31171      /**
31172      * Mark this field as invalid
31173      * @param {String} msg The validation message
31174      */
31175     markInvalid : function(msg)
31176     {
31177         
31178         var label = this.el.select('label', true).first();
31179         var icon = this.el.select('i.fa-star', true).first();
31180
31181         if(label && !icon){
31182             this.el.select('.roo-date-split-field-label', true).createChild({
31183                 tag : 'i',
31184                 cls : 'text-danger fa fa-lg fa-star',
31185                 tooltip : 'This field is required',
31186                 style : 'margin-right:5px;'
31187             }, label, true);
31188         }
31189         
31190         this.fireEvent('invalid', this, msg);
31191     },
31192     
31193     clearInvalid : function()
31194     {
31195         var label = this.el.select('label', true).first();
31196         var icon = this.el.select('i.fa-star', true).first();
31197
31198         if(label && icon){
31199             icon.remove();
31200         }
31201         
31202         this.fireEvent('valid', this);
31203     },
31204     
31205     getName: function()
31206     {
31207         return this.name;
31208     }
31209     
31210 });
31211
31212  /**
31213  *
31214  * This is based on 
31215  * http://masonry.desandro.com
31216  *
31217  * The idea is to render all the bricks based on vertical width...
31218  *
31219  * The original code extends 'outlayer' - we might need to use that....
31220  * 
31221  */
31222
31223
31224 /**
31225  * @class Roo.bootstrap.LayoutMasonry
31226  * @extends Roo.bootstrap.Component
31227  * Bootstrap Layout Masonry class
31228  * 
31229  * @constructor
31230  * Create a new Element
31231  * @param {Object} config The config object
31232  */
31233
31234 Roo.bootstrap.LayoutMasonry = function(config){
31235     
31236     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31237     
31238     this.bricks = [];
31239     
31240     Roo.bootstrap.LayoutMasonry.register(this);
31241     
31242     this.addEvents({
31243         // raw events
31244         /**
31245          * @event layout
31246          * Fire after layout the items
31247          * @param {Roo.bootstrap.LayoutMasonry} this
31248          * @param {Roo.EventObject} e
31249          */
31250         "layout" : true
31251     });
31252     
31253 };
31254
31255 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31256     
31257     /**
31258      * @cfg {Boolean} isLayoutInstant = no animation?
31259      */   
31260     isLayoutInstant : false, // needed?
31261    
31262     /**
31263      * @cfg {Number} boxWidth  width of the columns
31264      */   
31265     boxWidth : 450,
31266     
31267       /**
31268      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31269      */   
31270     boxHeight : 0,
31271     
31272     /**
31273      * @cfg {Number} padWidth padding below box..
31274      */   
31275     padWidth : 10, 
31276     
31277     /**
31278      * @cfg {Number} gutter gutter width..
31279      */   
31280     gutter : 10,
31281     
31282      /**
31283      * @cfg {Number} maxCols maximum number of columns
31284      */   
31285     
31286     maxCols: 0,
31287     
31288     /**
31289      * @cfg {Boolean} isAutoInitial defalut true
31290      */   
31291     isAutoInitial : true, 
31292     
31293     containerWidth: 0,
31294     
31295     /**
31296      * @cfg {Boolean} isHorizontal defalut false
31297      */   
31298     isHorizontal : false, 
31299
31300     currentSize : null,
31301     
31302     tag: 'div',
31303     
31304     cls: '',
31305     
31306     bricks: null, //CompositeElement
31307     
31308     cols : 1,
31309     
31310     _isLayoutInited : false,
31311     
31312 //    isAlternative : false, // only use for vertical layout...
31313     
31314     /**
31315      * @cfg {Number} alternativePadWidth padding below box..
31316      */   
31317     alternativePadWidth : 50,
31318     
31319     selectedBrick : [],
31320     
31321     getAutoCreate : function(){
31322         
31323         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31324         
31325         var cfg = {
31326             tag: this.tag,
31327             cls: 'blog-masonary-wrapper ' + this.cls,
31328             cn : {
31329                 cls : 'mas-boxes masonary'
31330             }
31331         };
31332         
31333         return cfg;
31334     },
31335     
31336     getChildContainer: function( )
31337     {
31338         if (this.boxesEl) {
31339             return this.boxesEl;
31340         }
31341         
31342         this.boxesEl = this.el.select('.mas-boxes').first();
31343         
31344         return this.boxesEl;
31345     },
31346     
31347     
31348     initEvents : function()
31349     {
31350         var _this = this;
31351         
31352         if(this.isAutoInitial){
31353             Roo.log('hook children rendered');
31354             this.on('childrenrendered', function() {
31355                 Roo.log('children rendered');
31356                 _this.initial();
31357             } ,this);
31358         }
31359     },
31360     
31361     initial : function()
31362     {
31363         this.selectedBrick = [];
31364         
31365         this.currentSize = this.el.getBox(true);
31366         
31367         Roo.EventManager.onWindowResize(this.resize, this); 
31368
31369         if(!this.isAutoInitial){
31370             this.layout();
31371             return;
31372         }
31373         
31374         this.layout();
31375         
31376         return;
31377         //this.layout.defer(500,this);
31378         
31379     },
31380     
31381     resize : function()
31382     {
31383         var cs = this.el.getBox(true);
31384         
31385         if (
31386                 this.currentSize.width == cs.width && 
31387                 this.currentSize.x == cs.x && 
31388                 this.currentSize.height == cs.height && 
31389                 this.currentSize.y == cs.y 
31390         ) {
31391             Roo.log("no change in with or X or Y");
31392             return;
31393         }
31394         
31395         this.currentSize = cs;
31396         
31397         this.layout();
31398         
31399     },
31400     
31401     layout : function()
31402     {   
31403         this._resetLayout();
31404         
31405         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31406         
31407         this.layoutItems( isInstant );
31408       
31409         this._isLayoutInited = true;
31410         
31411         this.fireEvent('layout', this);
31412         
31413     },
31414     
31415     _resetLayout : function()
31416     {
31417         if(this.isHorizontal){
31418             this.horizontalMeasureColumns();
31419             return;
31420         }
31421         
31422         this.verticalMeasureColumns();
31423         
31424     },
31425     
31426     verticalMeasureColumns : function()
31427     {
31428         this.getContainerWidth();
31429         
31430 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31431 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31432 //            return;
31433 //        }
31434         
31435         var boxWidth = this.boxWidth + this.padWidth;
31436         
31437         if(this.containerWidth < this.boxWidth){
31438             boxWidth = this.containerWidth
31439         }
31440         
31441         var containerWidth = this.containerWidth;
31442         
31443         var cols = Math.floor(containerWidth / boxWidth);
31444         
31445         this.cols = Math.max( cols, 1 );
31446         
31447         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31448         
31449         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31450         
31451         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31452         
31453         this.colWidth = boxWidth + avail - this.padWidth;
31454         
31455         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31456         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31457     },
31458     
31459     horizontalMeasureColumns : function()
31460     {
31461         this.getContainerWidth();
31462         
31463         var boxWidth = this.boxWidth;
31464         
31465         if(this.containerWidth < boxWidth){
31466             boxWidth = this.containerWidth;
31467         }
31468         
31469         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31470         
31471         this.el.setHeight(boxWidth);
31472         
31473     },
31474     
31475     getContainerWidth : function()
31476     {
31477         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31478     },
31479     
31480     layoutItems : function( isInstant )
31481     {
31482         Roo.log(this.bricks);
31483         
31484         var items = Roo.apply([], this.bricks);
31485         
31486         if(this.isHorizontal){
31487             this._horizontalLayoutItems( items , isInstant );
31488             return;
31489         }
31490         
31491 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31492 //            this._verticalAlternativeLayoutItems( items , isInstant );
31493 //            return;
31494 //        }
31495         
31496         this._verticalLayoutItems( items , isInstant );
31497         
31498     },
31499     
31500     _verticalLayoutItems : function ( items , isInstant)
31501     {
31502         if ( !items || !items.length ) {
31503             return;
31504         }
31505         
31506         var standard = [
31507             ['xs', 'xs', 'xs', 'tall'],
31508             ['xs', 'xs', 'tall'],
31509             ['xs', 'xs', 'sm'],
31510             ['xs', 'xs', 'xs'],
31511             ['xs', 'tall'],
31512             ['xs', 'sm'],
31513             ['xs', 'xs'],
31514             ['xs'],
31515             
31516             ['sm', 'xs', 'xs'],
31517             ['sm', 'xs'],
31518             ['sm'],
31519             
31520             ['tall', 'xs', 'xs', 'xs'],
31521             ['tall', 'xs', 'xs'],
31522             ['tall', 'xs'],
31523             ['tall']
31524             
31525         ];
31526         
31527         var queue = [];
31528         
31529         var boxes = [];
31530         
31531         var box = [];
31532         
31533         Roo.each(items, function(item, k){
31534             
31535             switch (item.size) {
31536                 // these layouts take up a full box,
31537                 case 'md' :
31538                 case 'md-left' :
31539                 case 'md-right' :
31540                 case 'wide' :
31541                     
31542                     if(box.length){
31543                         boxes.push(box);
31544                         box = [];
31545                     }
31546                     
31547                     boxes.push([item]);
31548                     
31549                     break;
31550                     
31551                 case 'xs' :
31552                 case 'sm' :
31553                 case 'tall' :
31554                     
31555                     box.push(item);
31556                     
31557                     break;
31558                 default :
31559                     break;
31560                     
31561             }
31562             
31563         }, this);
31564         
31565         if(box.length){
31566             boxes.push(box);
31567             box = [];
31568         }
31569         
31570         var filterPattern = function(box, length)
31571         {
31572             if(!box.length){
31573                 return;
31574             }
31575             
31576             var match = false;
31577             
31578             var pattern = box.slice(0, length);
31579             
31580             var format = [];
31581             
31582             Roo.each(pattern, function(i){
31583                 format.push(i.size);
31584             }, this);
31585             
31586             Roo.each(standard, function(s){
31587                 
31588                 if(String(s) != String(format)){
31589                     return;
31590                 }
31591                 
31592                 match = true;
31593                 return false;
31594                 
31595             }, this);
31596             
31597             if(!match && length == 1){
31598                 return;
31599             }
31600             
31601             if(!match){
31602                 filterPattern(box, length - 1);
31603                 return;
31604             }
31605                 
31606             queue.push(pattern);
31607
31608             box = box.slice(length, box.length);
31609
31610             filterPattern(box, 4);
31611
31612             return;
31613             
31614         }
31615         
31616         Roo.each(boxes, function(box, k){
31617             
31618             if(!box.length){
31619                 return;
31620             }
31621             
31622             if(box.length == 1){
31623                 queue.push(box);
31624                 return;
31625             }
31626             
31627             filterPattern(box, 4);
31628             
31629         }, this);
31630         
31631         this._processVerticalLayoutQueue( queue, isInstant );
31632         
31633     },
31634     
31635 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31636 //    {
31637 //        if ( !items || !items.length ) {
31638 //            return;
31639 //        }
31640 //
31641 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31642 //        
31643 //    },
31644     
31645     _horizontalLayoutItems : function ( items , isInstant)
31646     {
31647         if ( !items || !items.length || items.length < 3) {
31648             return;
31649         }
31650         
31651         items.reverse();
31652         
31653         var eItems = items.slice(0, 3);
31654         
31655         items = items.slice(3, items.length);
31656         
31657         var standard = [
31658             ['xs', 'xs', 'xs', 'wide'],
31659             ['xs', 'xs', 'wide'],
31660             ['xs', 'xs', 'sm'],
31661             ['xs', 'xs', 'xs'],
31662             ['xs', 'wide'],
31663             ['xs', 'sm'],
31664             ['xs', 'xs'],
31665             ['xs'],
31666             
31667             ['sm', 'xs', 'xs'],
31668             ['sm', 'xs'],
31669             ['sm'],
31670             
31671             ['wide', 'xs', 'xs', 'xs'],
31672             ['wide', 'xs', 'xs'],
31673             ['wide', 'xs'],
31674             ['wide'],
31675             
31676             ['wide-thin']
31677         ];
31678         
31679         var queue = [];
31680         
31681         var boxes = [];
31682         
31683         var box = [];
31684         
31685         Roo.each(items, function(item, k){
31686             
31687             switch (item.size) {
31688                 case 'md' :
31689                 case 'md-left' :
31690                 case 'md-right' :
31691                 case 'tall' :
31692                     
31693                     if(box.length){
31694                         boxes.push(box);
31695                         box = [];
31696                     }
31697                     
31698                     boxes.push([item]);
31699                     
31700                     break;
31701                     
31702                 case 'xs' :
31703                 case 'sm' :
31704                 case 'wide' :
31705                 case 'wide-thin' :
31706                     
31707                     box.push(item);
31708                     
31709                     break;
31710                 default :
31711                     break;
31712                     
31713             }
31714             
31715         }, this);
31716         
31717         if(box.length){
31718             boxes.push(box);
31719             box = [];
31720         }
31721         
31722         var filterPattern = function(box, length)
31723         {
31724             if(!box.length){
31725                 return;
31726             }
31727             
31728             var match = false;
31729             
31730             var pattern = box.slice(0, length);
31731             
31732             var format = [];
31733             
31734             Roo.each(pattern, function(i){
31735                 format.push(i.size);
31736             }, this);
31737             
31738             Roo.each(standard, function(s){
31739                 
31740                 if(String(s) != String(format)){
31741                     return;
31742                 }
31743                 
31744                 match = true;
31745                 return false;
31746                 
31747             }, this);
31748             
31749             if(!match && length == 1){
31750                 return;
31751             }
31752             
31753             if(!match){
31754                 filterPattern(box, length - 1);
31755                 return;
31756             }
31757                 
31758             queue.push(pattern);
31759
31760             box = box.slice(length, box.length);
31761
31762             filterPattern(box, 4);
31763
31764             return;
31765             
31766         }
31767         
31768         Roo.each(boxes, function(box, k){
31769             
31770             if(!box.length){
31771                 return;
31772             }
31773             
31774             if(box.length == 1){
31775                 queue.push(box);
31776                 return;
31777             }
31778             
31779             filterPattern(box, 4);
31780             
31781         }, this);
31782         
31783         
31784         var prune = [];
31785         
31786         var pos = this.el.getBox(true);
31787         
31788         var minX = pos.x;
31789         
31790         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31791         
31792         var hit_end = false;
31793         
31794         Roo.each(queue, function(box){
31795             
31796             if(hit_end){
31797                 
31798                 Roo.each(box, function(b){
31799                 
31800                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31801                     b.el.hide();
31802
31803                 }, this);
31804
31805                 return;
31806             }
31807             
31808             var mx = 0;
31809             
31810             Roo.each(box, function(b){
31811                 
31812                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31813                 b.el.show();
31814
31815                 mx = Math.max(mx, b.x);
31816                 
31817             }, this);
31818             
31819             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31820             
31821             if(maxX < minX){
31822                 
31823                 Roo.each(box, function(b){
31824                 
31825                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31826                     b.el.hide();
31827                     
31828                 }, this);
31829                 
31830                 hit_end = true;
31831                 
31832                 return;
31833             }
31834             
31835             prune.push(box);
31836             
31837         }, this);
31838         
31839         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31840     },
31841     
31842     /** Sets position of item in DOM
31843     * @param {Element} item
31844     * @param {Number} x - horizontal position
31845     * @param {Number} y - vertical position
31846     * @param {Boolean} isInstant - disables transitions
31847     */
31848     _processVerticalLayoutQueue : function( queue, isInstant )
31849     {
31850         var pos = this.el.getBox(true);
31851         var x = pos.x;
31852         var y = pos.y;
31853         var maxY = [];
31854         
31855         for (var i = 0; i < this.cols; i++){
31856             maxY[i] = pos.y;
31857         }
31858         
31859         Roo.each(queue, function(box, k){
31860             
31861             var col = k % this.cols;
31862             
31863             Roo.each(box, function(b,kk){
31864                 
31865                 b.el.position('absolute');
31866                 
31867                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31868                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31869                 
31870                 if(b.size == 'md-left' || b.size == 'md-right'){
31871                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31872                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31873                 }
31874                 
31875                 b.el.setWidth(width);
31876                 b.el.setHeight(height);
31877                 // iframe?
31878                 b.el.select('iframe',true).setSize(width,height);
31879                 
31880             }, this);
31881             
31882             for (var i = 0; i < this.cols; i++){
31883                 
31884                 if(maxY[i] < maxY[col]){
31885                     col = i;
31886                     continue;
31887                 }
31888                 
31889                 col = Math.min(col, i);
31890                 
31891             }
31892             
31893             x = pos.x + col * (this.colWidth + this.padWidth);
31894             
31895             y = maxY[col];
31896             
31897             var positions = [];
31898             
31899             switch (box.length){
31900                 case 1 :
31901                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31902                     break;
31903                 case 2 :
31904                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31905                     break;
31906                 case 3 :
31907                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31908                     break;
31909                 case 4 :
31910                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31911                     break;
31912                 default :
31913                     break;
31914             }
31915             
31916             Roo.each(box, function(b,kk){
31917                 
31918                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31919                 
31920                 var sz = b.el.getSize();
31921                 
31922                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31923                 
31924             }, this);
31925             
31926         }, this);
31927         
31928         var mY = 0;
31929         
31930         for (var i = 0; i < this.cols; i++){
31931             mY = Math.max(mY, maxY[i]);
31932         }
31933         
31934         this.el.setHeight(mY - pos.y);
31935         
31936     },
31937     
31938 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31939 //    {
31940 //        var pos = this.el.getBox(true);
31941 //        var x = pos.x;
31942 //        var y = pos.y;
31943 //        var maxX = pos.right;
31944 //        
31945 //        var maxHeight = 0;
31946 //        
31947 //        Roo.each(items, function(item, k){
31948 //            
31949 //            var c = k % 2;
31950 //            
31951 //            item.el.position('absolute');
31952 //                
31953 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31954 //
31955 //            item.el.setWidth(width);
31956 //
31957 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31958 //
31959 //            item.el.setHeight(height);
31960 //            
31961 //            if(c == 0){
31962 //                item.el.setXY([x, y], isInstant ? false : true);
31963 //            } else {
31964 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31965 //            }
31966 //            
31967 //            y = y + height + this.alternativePadWidth;
31968 //            
31969 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31970 //            
31971 //        }, this);
31972 //        
31973 //        this.el.setHeight(maxHeight);
31974 //        
31975 //    },
31976     
31977     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31978     {
31979         var pos = this.el.getBox(true);
31980         
31981         var minX = pos.x;
31982         var minY = pos.y;
31983         
31984         var maxX = pos.right;
31985         
31986         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31987         
31988         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31989         
31990         Roo.each(queue, function(box, k){
31991             
31992             Roo.each(box, function(b, kk){
31993                 
31994                 b.el.position('absolute');
31995                 
31996                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31997                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31998                 
31999                 if(b.size == 'md-left' || b.size == 'md-right'){
32000                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
32001                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
32002                 }
32003                 
32004                 b.el.setWidth(width);
32005                 b.el.setHeight(height);
32006                 
32007             }, this);
32008             
32009             if(!box.length){
32010                 return;
32011             }
32012             
32013             var positions = [];
32014             
32015             switch (box.length){
32016                 case 1 :
32017                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32018                     break;
32019                 case 2 :
32020                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32021                     break;
32022                 case 3 :
32023                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32024                     break;
32025                 case 4 :
32026                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32027                     break;
32028                 default :
32029                     break;
32030             }
32031             
32032             Roo.each(box, function(b,kk){
32033                 
32034                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32035                 
32036                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32037                 
32038             }, this);
32039             
32040         }, this);
32041         
32042     },
32043     
32044     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32045     {
32046         Roo.each(eItems, function(b,k){
32047             
32048             b.size = (k == 0) ? 'sm' : 'xs';
32049             b.x = (k == 0) ? 2 : 1;
32050             b.y = (k == 0) ? 2 : 1;
32051             
32052             b.el.position('absolute');
32053             
32054             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32055                 
32056             b.el.setWidth(width);
32057             
32058             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32059             
32060             b.el.setHeight(height);
32061             
32062         }, this);
32063
32064         var positions = [];
32065         
32066         positions.push({
32067             x : maxX - this.unitWidth * 2 - this.gutter,
32068             y : minY
32069         });
32070         
32071         positions.push({
32072             x : maxX - this.unitWidth,
32073             y : minY + (this.unitWidth + this.gutter) * 2
32074         });
32075         
32076         positions.push({
32077             x : maxX - this.unitWidth * 3 - this.gutter * 2,
32078             y : minY
32079         });
32080         
32081         Roo.each(eItems, function(b,k){
32082             
32083             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32084
32085         }, this);
32086         
32087     },
32088     
32089     getVerticalOneBoxColPositions : function(x, y, box)
32090     {
32091         var pos = [];
32092         
32093         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32094         
32095         if(box[0].size == 'md-left'){
32096             rand = 0;
32097         }
32098         
32099         if(box[0].size == 'md-right'){
32100             rand = 1;
32101         }
32102         
32103         pos.push({
32104             x : x + (this.unitWidth + this.gutter) * rand,
32105             y : y
32106         });
32107         
32108         return pos;
32109     },
32110     
32111     getVerticalTwoBoxColPositions : function(x, y, box)
32112     {
32113         var pos = [];
32114         
32115         if(box[0].size == 'xs'){
32116             
32117             pos.push({
32118                 x : x,
32119                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32120             });
32121
32122             pos.push({
32123                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32124                 y : y
32125             });
32126             
32127             return pos;
32128             
32129         }
32130         
32131         pos.push({
32132             x : x,
32133             y : y
32134         });
32135
32136         pos.push({
32137             x : x + (this.unitWidth + this.gutter) * 2,
32138             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32139         });
32140         
32141         return pos;
32142         
32143     },
32144     
32145     getVerticalThreeBoxColPositions : function(x, y, box)
32146     {
32147         var pos = [];
32148         
32149         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32150             
32151             pos.push({
32152                 x : x,
32153                 y : y
32154             });
32155
32156             pos.push({
32157                 x : x + (this.unitWidth + this.gutter) * 1,
32158                 y : y
32159             });
32160             
32161             pos.push({
32162                 x : x + (this.unitWidth + this.gutter) * 2,
32163                 y : y
32164             });
32165             
32166             return pos;
32167             
32168         }
32169         
32170         if(box[0].size == 'xs' && box[1].size == 'xs'){
32171             
32172             pos.push({
32173                 x : x,
32174                 y : y
32175             });
32176
32177             pos.push({
32178                 x : x,
32179                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32180             });
32181             
32182             pos.push({
32183                 x : x + (this.unitWidth + this.gutter) * 1,
32184                 y : y
32185             });
32186             
32187             return pos;
32188             
32189         }
32190         
32191         pos.push({
32192             x : x,
32193             y : y
32194         });
32195
32196         pos.push({
32197             x : x + (this.unitWidth + this.gutter) * 2,
32198             y : y
32199         });
32200
32201         pos.push({
32202             x : x + (this.unitWidth + this.gutter) * 2,
32203             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32204         });
32205             
32206         return pos;
32207         
32208     },
32209     
32210     getVerticalFourBoxColPositions : function(x, y, box)
32211     {
32212         var pos = [];
32213         
32214         if(box[0].size == 'xs'){
32215             
32216             pos.push({
32217                 x : x,
32218                 y : y
32219             });
32220
32221             pos.push({
32222                 x : x,
32223                 y : y + (this.unitHeight + this.gutter) * 1
32224             });
32225             
32226             pos.push({
32227                 x : x,
32228                 y : y + (this.unitHeight + this.gutter) * 2
32229             });
32230             
32231             pos.push({
32232                 x : x + (this.unitWidth + this.gutter) * 1,
32233                 y : y
32234             });
32235             
32236             return pos;
32237             
32238         }
32239         
32240         pos.push({
32241             x : x,
32242             y : y
32243         });
32244
32245         pos.push({
32246             x : x + (this.unitWidth + this.gutter) * 2,
32247             y : y
32248         });
32249
32250         pos.push({
32251             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32252             y : y + (this.unitHeight + this.gutter) * 1
32253         });
32254
32255         pos.push({
32256             x : x + (this.unitWidth + this.gutter) * 2,
32257             y : y + (this.unitWidth + this.gutter) * 2
32258         });
32259
32260         return pos;
32261         
32262     },
32263     
32264     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32265     {
32266         var pos = [];
32267         
32268         if(box[0].size == 'md-left'){
32269             pos.push({
32270                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32271                 y : minY
32272             });
32273             
32274             return pos;
32275         }
32276         
32277         if(box[0].size == 'md-right'){
32278             pos.push({
32279                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32280                 y : minY + (this.unitWidth + this.gutter) * 1
32281             });
32282             
32283             return pos;
32284         }
32285         
32286         var rand = Math.floor(Math.random() * (4 - box[0].y));
32287         
32288         pos.push({
32289             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32290             y : minY + (this.unitWidth + this.gutter) * rand
32291         });
32292         
32293         return pos;
32294         
32295     },
32296     
32297     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32298     {
32299         var pos = [];
32300         
32301         if(box[0].size == 'xs'){
32302             
32303             pos.push({
32304                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32305                 y : minY
32306             });
32307
32308             pos.push({
32309                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32310                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32311             });
32312             
32313             return pos;
32314             
32315         }
32316         
32317         pos.push({
32318             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32319             y : minY
32320         });
32321
32322         pos.push({
32323             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32324             y : minY + (this.unitWidth + this.gutter) * 2
32325         });
32326         
32327         return pos;
32328         
32329     },
32330     
32331     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32332     {
32333         var pos = [];
32334         
32335         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32336             
32337             pos.push({
32338                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32339                 y : minY
32340             });
32341
32342             pos.push({
32343                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32344                 y : minY + (this.unitWidth + this.gutter) * 1
32345             });
32346             
32347             pos.push({
32348                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32349                 y : minY + (this.unitWidth + this.gutter) * 2
32350             });
32351             
32352             return pos;
32353             
32354         }
32355         
32356         if(box[0].size == 'xs' && box[1].size == 'xs'){
32357             
32358             pos.push({
32359                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32360                 y : minY
32361             });
32362
32363             pos.push({
32364                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32365                 y : minY
32366             });
32367             
32368             pos.push({
32369                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32370                 y : minY + (this.unitWidth + this.gutter) * 1
32371             });
32372             
32373             return pos;
32374             
32375         }
32376         
32377         pos.push({
32378             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32379             y : minY
32380         });
32381
32382         pos.push({
32383             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32384             y : minY + (this.unitWidth + this.gutter) * 2
32385         });
32386
32387         pos.push({
32388             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32389             y : minY + (this.unitWidth + this.gutter) * 2
32390         });
32391             
32392         return pos;
32393         
32394     },
32395     
32396     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32397     {
32398         var pos = [];
32399         
32400         if(box[0].size == 'xs'){
32401             
32402             pos.push({
32403                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32404                 y : minY
32405             });
32406
32407             pos.push({
32408                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32409                 y : minY
32410             });
32411             
32412             pos.push({
32413                 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),
32414                 y : minY
32415             });
32416             
32417             pos.push({
32418                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32419                 y : minY + (this.unitWidth + this.gutter) * 1
32420             });
32421             
32422             return pos;
32423             
32424         }
32425         
32426         pos.push({
32427             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32428             y : minY
32429         });
32430         
32431         pos.push({
32432             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32433             y : minY + (this.unitWidth + this.gutter) * 2
32434         });
32435         
32436         pos.push({
32437             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32438             y : minY + (this.unitWidth + this.gutter) * 2
32439         });
32440         
32441         pos.push({
32442             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),
32443             y : minY + (this.unitWidth + this.gutter) * 2
32444         });
32445
32446         return pos;
32447         
32448     },
32449     
32450     /**
32451     * remove a Masonry Brick
32452     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32453     */
32454     removeBrick : function(brick_id)
32455     {
32456         if (!brick_id) {
32457             return;
32458         }
32459         
32460         for (var i = 0; i<this.bricks.length; i++) {
32461             if (this.bricks[i].id == brick_id) {
32462                 this.bricks.splice(i,1);
32463                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32464                 this.initial();
32465             }
32466         }
32467     },
32468     
32469     /**
32470     * adds a Masonry Brick
32471     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32472     */
32473     addBrick : function(cfg)
32474     {
32475         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32476         //this.register(cn);
32477         cn.parentId = this.id;
32478         cn.render(this.el);
32479         return cn;
32480     },
32481     
32482     /**
32483     * register a Masonry Brick
32484     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32485     */
32486     
32487     register : function(brick)
32488     {
32489         this.bricks.push(brick);
32490         brick.masonryId = this.id;
32491     },
32492     
32493     /**
32494     * clear all the Masonry Brick
32495     */
32496     clearAll : function()
32497     {
32498         this.bricks = [];
32499         //this.getChildContainer().dom.innerHTML = "";
32500         this.el.dom.innerHTML = '';
32501     },
32502     
32503     getSelected : function()
32504     {
32505         if (!this.selectedBrick) {
32506             return false;
32507         }
32508         
32509         return this.selectedBrick;
32510     }
32511 });
32512
32513 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32514     
32515     groups: {},
32516      /**
32517     * register a Masonry Layout
32518     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32519     */
32520     
32521     register : function(layout)
32522     {
32523         this.groups[layout.id] = layout;
32524     },
32525     /**
32526     * fetch a  Masonry Layout based on the masonry layout ID
32527     * @param {string} the masonry layout to add
32528     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32529     */
32530     
32531     get: function(layout_id) {
32532         if (typeof(this.groups[layout_id]) == 'undefined') {
32533             return false;
32534         }
32535         return this.groups[layout_id] ;
32536     }
32537     
32538     
32539     
32540 });
32541
32542  
32543
32544  /**
32545  *
32546  * This is based on 
32547  * http://masonry.desandro.com
32548  *
32549  * The idea is to render all the bricks based on vertical width...
32550  *
32551  * The original code extends 'outlayer' - we might need to use that....
32552  * 
32553  */
32554
32555
32556 /**
32557  * @class Roo.bootstrap.LayoutMasonryAuto
32558  * @extends Roo.bootstrap.Component
32559  * Bootstrap Layout Masonry class
32560  * 
32561  * @constructor
32562  * Create a new Element
32563  * @param {Object} config The config object
32564  */
32565
32566 Roo.bootstrap.LayoutMasonryAuto = function(config){
32567     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32568 };
32569
32570 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32571     
32572       /**
32573      * @cfg {Boolean} isFitWidth  - resize the width..
32574      */   
32575     isFitWidth : false,  // options..
32576     /**
32577      * @cfg {Boolean} isOriginLeft = left align?
32578      */   
32579     isOriginLeft : true,
32580     /**
32581      * @cfg {Boolean} isOriginTop = top align?
32582      */   
32583     isOriginTop : false,
32584     /**
32585      * @cfg {Boolean} isLayoutInstant = no animation?
32586      */   
32587     isLayoutInstant : false, // needed?
32588     /**
32589      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32590      */   
32591     isResizingContainer : true,
32592     /**
32593      * @cfg {Number} columnWidth  width of the columns 
32594      */   
32595     
32596     columnWidth : 0,
32597     
32598     /**
32599      * @cfg {Number} maxCols maximum number of columns
32600      */   
32601     
32602     maxCols: 0,
32603     /**
32604      * @cfg {Number} padHeight padding below box..
32605      */   
32606     
32607     padHeight : 10, 
32608     
32609     /**
32610      * @cfg {Boolean} isAutoInitial defalut true
32611      */   
32612     
32613     isAutoInitial : true, 
32614     
32615     // private?
32616     gutter : 0,
32617     
32618     containerWidth: 0,
32619     initialColumnWidth : 0,
32620     currentSize : null,
32621     
32622     colYs : null, // array.
32623     maxY : 0,
32624     padWidth: 10,
32625     
32626     
32627     tag: 'div',
32628     cls: '',
32629     bricks: null, //CompositeElement
32630     cols : 0, // array?
32631     // element : null, // wrapped now this.el
32632     _isLayoutInited : null, 
32633     
32634     
32635     getAutoCreate : function(){
32636         
32637         var cfg = {
32638             tag: this.tag,
32639             cls: 'blog-masonary-wrapper ' + this.cls,
32640             cn : {
32641                 cls : 'mas-boxes masonary'
32642             }
32643         };
32644         
32645         return cfg;
32646     },
32647     
32648     getChildContainer: function( )
32649     {
32650         if (this.boxesEl) {
32651             return this.boxesEl;
32652         }
32653         
32654         this.boxesEl = this.el.select('.mas-boxes').first();
32655         
32656         return this.boxesEl;
32657     },
32658     
32659     
32660     initEvents : function()
32661     {
32662         var _this = this;
32663         
32664         if(this.isAutoInitial){
32665             Roo.log('hook children rendered');
32666             this.on('childrenrendered', function() {
32667                 Roo.log('children rendered');
32668                 _this.initial();
32669             } ,this);
32670         }
32671         
32672     },
32673     
32674     initial : function()
32675     {
32676         this.reloadItems();
32677
32678         this.currentSize = this.el.getBox(true);
32679
32680         /// was window resize... - let's see if this works..
32681         Roo.EventManager.onWindowResize(this.resize, this); 
32682
32683         if(!this.isAutoInitial){
32684             this.layout();
32685             return;
32686         }
32687         
32688         this.layout.defer(500,this);
32689     },
32690     
32691     reloadItems: function()
32692     {
32693         this.bricks = this.el.select('.masonry-brick', true);
32694         
32695         this.bricks.each(function(b) {
32696             //Roo.log(b.getSize());
32697             if (!b.attr('originalwidth')) {
32698                 b.attr('originalwidth',  b.getSize().width);
32699             }
32700             
32701         });
32702         
32703         Roo.log(this.bricks.elements.length);
32704     },
32705     
32706     resize : function()
32707     {
32708         Roo.log('resize');
32709         var cs = this.el.getBox(true);
32710         
32711         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32712             Roo.log("no change in with or X");
32713             return;
32714         }
32715         this.currentSize = cs;
32716         this.layout();
32717     },
32718     
32719     layout : function()
32720     {
32721          Roo.log('layout');
32722         this._resetLayout();
32723         //this._manageStamps();
32724       
32725         // don't animate first layout
32726         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32727         this.layoutItems( isInstant );
32728       
32729         // flag for initalized
32730         this._isLayoutInited = true;
32731     },
32732     
32733     layoutItems : function( isInstant )
32734     {
32735         //var items = this._getItemsForLayout( this.items );
32736         // original code supports filtering layout items.. we just ignore it..
32737         
32738         this._layoutItems( this.bricks , isInstant );
32739       
32740         this._postLayout();
32741     },
32742     _layoutItems : function ( items , isInstant)
32743     {
32744        //this.fireEvent( 'layout', this, items );
32745     
32746
32747         if ( !items || !items.elements.length ) {
32748           // no items, emit event with empty array
32749             return;
32750         }
32751
32752         var queue = [];
32753         items.each(function(item) {
32754             Roo.log("layout item");
32755             Roo.log(item);
32756             // get x/y object from method
32757             var position = this._getItemLayoutPosition( item );
32758             // enqueue
32759             position.item = item;
32760             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32761             queue.push( position );
32762         }, this);
32763       
32764         this._processLayoutQueue( queue );
32765     },
32766     /** Sets position of item in DOM
32767     * @param {Element} item
32768     * @param {Number} x - horizontal position
32769     * @param {Number} y - vertical position
32770     * @param {Boolean} isInstant - disables transitions
32771     */
32772     _processLayoutQueue : function( queue )
32773     {
32774         for ( var i=0, len = queue.length; i < len; i++ ) {
32775             var obj = queue[i];
32776             obj.item.position('absolute');
32777             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32778         }
32779     },
32780       
32781     
32782     /**
32783     * Any logic you want to do after each layout,
32784     * i.e. size the container
32785     */
32786     _postLayout : function()
32787     {
32788         this.resizeContainer();
32789     },
32790     
32791     resizeContainer : function()
32792     {
32793         if ( !this.isResizingContainer ) {
32794             return;
32795         }
32796         var size = this._getContainerSize();
32797         if ( size ) {
32798             this.el.setSize(size.width,size.height);
32799             this.boxesEl.setSize(size.width,size.height);
32800         }
32801     },
32802     
32803     
32804     
32805     _resetLayout : function()
32806     {
32807         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32808         this.colWidth = this.el.getWidth();
32809         //this.gutter = this.el.getWidth(); 
32810         
32811         this.measureColumns();
32812
32813         // reset column Y
32814         var i = this.cols;
32815         this.colYs = [];
32816         while (i--) {
32817             this.colYs.push( 0 );
32818         }
32819     
32820         this.maxY = 0;
32821     },
32822
32823     measureColumns : function()
32824     {
32825         this.getContainerWidth();
32826       // if columnWidth is 0, default to outerWidth of first item
32827         if ( !this.columnWidth ) {
32828             var firstItem = this.bricks.first();
32829             Roo.log(firstItem);
32830             this.columnWidth  = this.containerWidth;
32831             if (firstItem && firstItem.attr('originalwidth') ) {
32832                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32833             }
32834             // columnWidth fall back to item of first element
32835             Roo.log("set column width?");
32836                         this.initialColumnWidth = this.columnWidth  ;
32837
32838             // if first elem has no width, default to size of container
32839             
32840         }
32841         
32842         
32843         if (this.initialColumnWidth) {
32844             this.columnWidth = this.initialColumnWidth;
32845         }
32846         
32847         
32848             
32849         // column width is fixed at the top - however if container width get's smaller we should
32850         // reduce it...
32851         
32852         // this bit calcs how man columns..
32853             
32854         var columnWidth = this.columnWidth += this.gutter;
32855       
32856         // calculate columns
32857         var containerWidth = this.containerWidth + this.gutter;
32858         
32859         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32860         // fix rounding errors, typically with gutters
32861         var excess = columnWidth - containerWidth % columnWidth;
32862         
32863         
32864         // if overshoot is less than a pixel, round up, otherwise floor it
32865         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32866         cols = Math[ mathMethod ]( cols );
32867         this.cols = Math.max( cols, 1 );
32868         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32869         
32870          // padding positioning..
32871         var totalColWidth = this.cols * this.columnWidth;
32872         var padavail = this.containerWidth - totalColWidth;
32873         // so for 2 columns - we need 3 'pads'
32874         
32875         var padNeeded = (1+this.cols) * this.padWidth;
32876         
32877         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32878         
32879         this.columnWidth += padExtra
32880         //this.padWidth = Math.floor(padavail /  ( this.cols));
32881         
32882         // adjust colum width so that padding is fixed??
32883         
32884         // we have 3 columns ... total = width * 3
32885         // we have X left over... that should be used by 
32886         
32887         //if (this.expandC) {
32888             
32889         //}
32890         
32891         
32892         
32893     },
32894     
32895     getContainerWidth : function()
32896     {
32897        /* // container is parent if fit width
32898         var container = this.isFitWidth ? this.element.parentNode : this.element;
32899         // check that this.size and size are there
32900         // IE8 triggers resize on body size change, so they might not be
32901         
32902         var size = getSize( container );  //FIXME
32903         this.containerWidth = size && size.innerWidth; //FIXME
32904         */
32905          
32906         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32907         
32908     },
32909     
32910     _getItemLayoutPosition : function( item )  // what is item?
32911     {
32912         // we resize the item to our columnWidth..
32913       
32914         item.setWidth(this.columnWidth);
32915         item.autoBoxAdjust  = false;
32916         
32917         var sz = item.getSize();
32918  
32919         // how many columns does this brick span
32920         var remainder = this.containerWidth % this.columnWidth;
32921         
32922         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32923         // round if off by 1 pixel, otherwise use ceil
32924         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32925         colSpan = Math.min( colSpan, this.cols );
32926         
32927         // normally this should be '1' as we dont' currently allow multi width columns..
32928         
32929         var colGroup = this._getColGroup( colSpan );
32930         // get the minimum Y value from the columns
32931         var minimumY = Math.min.apply( Math, colGroup );
32932         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32933         
32934         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32935          
32936         // position the brick
32937         var position = {
32938             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32939             y: this.currentSize.y + minimumY + this.padHeight
32940         };
32941         
32942         Roo.log(position);
32943         // apply setHeight to necessary columns
32944         var setHeight = minimumY + sz.height + this.padHeight;
32945         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32946         
32947         var setSpan = this.cols + 1 - colGroup.length;
32948         for ( var i = 0; i < setSpan; i++ ) {
32949           this.colYs[ shortColIndex + i ] = setHeight ;
32950         }
32951       
32952         return position;
32953     },
32954     
32955     /**
32956      * @param {Number} colSpan - number of columns the element spans
32957      * @returns {Array} colGroup
32958      */
32959     _getColGroup : function( colSpan )
32960     {
32961         if ( colSpan < 2 ) {
32962           // if brick spans only one column, use all the column Ys
32963           return this.colYs;
32964         }
32965       
32966         var colGroup = [];
32967         // how many different places could this brick fit horizontally
32968         var groupCount = this.cols + 1 - colSpan;
32969         // for each group potential horizontal position
32970         for ( var i = 0; i < groupCount; i++ ) {
32971           // make an array of colY values for that one group
32972           var groupColYs = this.colYs.slice( i, i + colSpan );
32973           // and get the max value of the array
32974           colGroup[i] = Math.max.apply( Math, groupColYs );
32975         }
32976         return colGroup;
32977     },
32978     /*
32979     _manageStamp : function( stamp )
32980     {
32981         var stampSize =  stamp.getSize();
32982         var offset = stamp.getBox();
32983         // get the columns that this stamp affects
32984         var firstX = this.isOriginLeft ? offset.x : offset.right;
32985         var lastX = firstX + stampSize.width;
32986         var firstCol = Math.floor( firstX / this.columnWidth );
32987         firstCol = Math.max( 0, firstCol );
32988         
32989         var lastCol = Math.floor( lastX / this.columnWidth );
32990         // lastCol should not go over if multiple of columnWidth #425
32991         lastCol -= lastX % this.columnWidth ? 0 : 1;
32992         lastCol = Math.min( this.cols - 1, lastCol );
32993         
32994         // set colYs to bottom of the stamp
32995         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32996             stampSize.height;
32997             
32998         for ( var i = firstCol; i <= lastCol; i++ ) {
32999           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
33000         }
33001     },
33002     */
33003     
33004     _getContainerSize : function()
33005     {
33006         this.maxY = Math.max.apply( Math, this.colYs );
33007         var size = {
33008             height: this.maxY
33009         };
33010       
33011         if ( this.isFitWidth ) {
33012             size.width = this._getContainerFitWidth();
33013         }
33014       
33015         return size;
33016     },
33017     
33018     _getContainerFitWidth : function()
33019     {
33020         var unusedCols = 0;
33021         // count unused columns
33022         var i = this.cols;
33023         while ( --i ) {
33024           if ( this.colYs[i] !== 0 ) {
33025             break;
33026           }
33027           unusedCols++;
33028         }
33029         // fit container to columns that have been used
33030         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33031     },
33032     
33033     needsResizeLayout : function()
33034     {
33035         var previousWidth = this.containerWidth;
33036         this.getContainerWidth();
33037         return previousWidth !== this.containerWidth;
33038     }
33039  
33040 });
33041
33042  
33043
33044  /*
33045  * - LGPL
33046  *
33047  * element
33048  * 
33049  */
33050
33051 /**
33052  * @class Roo.bootstrap.MasonryBrick
33053  * @extends Roo.bootstrap.Component
33054  * Bootstrap MasonryBrick class
33055  * 
33056  * @constructor
33057  * Create a new MasonryBrick
33058  * @param {Object} config The config object
33059  */
33060
33061 Roo.bootstrap.MasonryBrick = function(config){
33062     
33063     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33064     
33065     Roo.bootstrap.MasonryBrick.register(this);
33066     
33067     this.addEvents({
33068         // raw events
33069         /**
33070          * @event click
33071          * When a MasonryBrick is clcik
33072          * @param {Roo.bootstrap.MasonryBrick} this
33073          * @param {Roo.EventObject} e
33074          */
33075         "click" : true
33076     });
33077 };
33078
33079 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
33080     
33081     /**
33082      * @cfg {String} title
33083      */   
33084     title : '',
33085     /**
33086      * @cfg {String} html
33087      */   
33088     html : '',
33089     /**
33090      * @cfg {String} bgimage
33091      */   
33092     bgimage : '',
33093     /**
33094      * @cfg {String} videourl
33095      */   
33096     videourl : '',
33097     /**
33098      * @cfg {String} cls
33099      */   
33100     cls : '',
33101     /**
33102      * @cfg {String} href
33103      */   
33104     href : '',
33105     /**
33106      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33107      */   
33108     size : 'xs',
33109     
33110     /**
33111      * @cfg {String} placetitle (center|bottom)
33112      */   
33113     placetitle : '',
33114     
33115     /**
33116      * @cfg {Boolean} isFitContainer defalut true
33117      */   
33118     isFitContainer : true, 
33119     
33120     /**
33121      * @cfg {Boolean} preventDefault defalut false
33122      */   
33123     preventDefault : false, 
33124     
33125     /**
33126      * @cfg {Boolean} inverse defalut false
33127      */   
33128     maskInverse : false, 
33129     
33130     getAutoCreate : function()
33131     {
33132         if(!this.isFitContainer){
33133             return this.getSplitAutoCreate();
33134         }
33135         
33136         var cls = 'masonry-brick masonry-brick-full';
33137         
33138         if(this.href.length){
33139             cls += ' masonry-brick-link';
33140         }
33141         
33142         if(this.bgimage.length){
33143             cls += ' masonry-brick-image';
33144         }
33145         
33146         if(this.maskInverse){
33147             cls += ' mask-inverse';
33148         }
33149         
33150         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33151             cls += ' enable-mask';
33152         }
33153         
33154         if(this.size){
33155             cls += ' masonry-' + this.size + '-brick';
33156         }
33157         
33158         if(this.placetitle.length){
33159             
33160             switch (this.placetitle) {
33161                 case 'center' :
33162                     cls += ' masonry-center-title';
33163                     break;
33164                 case 'bottom' :
33165                     cls += ' masonry-bottom-title';
33166                     break;
33167                 default:
33168                     break;
33169             }
33170             
33171         } else {
33172             if(!this.html.length && !this.bgimage.length){
33173                 cls += ' masonry-center-title';
33174             }
33175
33176             if(!this.html.length && this.bgimage.length){
33177                 cls += ' masonry-bottom-title';
33178             }
33179         }
33180         
33181         if(this.cls){
33182             cls += ' ' + this.cls;
33183         }
33184         
33185         var cfg = {
33186             tag: (this.href.length) ? 'a' : 'div',
33187             cls: cls,
33188             cn: [
33189                 {
33190                     tag: 'div',
33191                     cls: 'masonry-brick-mask'
33192                 },
33193                 {
33194                     tag: 'div',
33195                     cls: 'masonry-brick-paragraph',
33196                     cn: []
33197                 }
33198             ]
33199         };
33200         
33201         if(this.href.length){
33202             cfg.href = this.href;
33203         }
33204         
33205         var cn = cfg.cn[1].cn;
33206         
33207         if(this.title.length){
33208             cn.push({
33209                 tag: 'h4',
33210                 cls: 'masonry-brick-title',
33211                 html: this.title
33212             });
33213         }
33214         
33215         if(this.html.length){
33216             cn.push({
33217                 tag: 'p',
33218                 cls: 'masonry-brick-text',
33219                 html: this.html
33220             });
33221         }
33222         
33223         if (!this.title.length && !this.html.length) {
33224             cfg.cn[1].cls += ' hide';
33225         }
33226         
33227         if(this.bgimage.length){
33228             cfg.cn.push({
33229                 tag: 'img',
33230                 cls: 'masonry-brick-image-view',
33231                 src: this.bgimage
33232             });
33233         }
33234         
33235         if(this.videourl.length){
33236             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33237             // youtube support only?
33238             cfg.cn.push({
33239                 tag: 'iframe',
33240                 cls: 'masonry-brick-image-view',
33241                 src: vurl,
33242                 frameborder : 0,
33243                 allowfullscreen : true
33244             });
33245         }
33246         
33247         return cfg;
33248         
33249     },
33250     
33251     getSplitAutoCreate : function()
33252     {
33253         var cls = 'masonry-brick masonry-brick-split';
33254         
33255         if(this.href.length){
33256             cls += ' masonry-brick-link';
33257         }
33258         
33259         if(this.bgimage.length){
33260             cls += ' masonry-brick-image';
33261         }
33262         
33263         if(this.size){
33264             cls += ' masonry-' + this.size + '-brick';
33265         }
33266         
33267         switch (this.placetitle) {
33268             case 'center' :
33269                 cls += ' masonry-center-title';
33270                 break;
33271             case 'bottom' :
33272                 cls += ' masonry-bottom-title';
33273                 break;
33274             default:
33275                 if(!this.bgimage.length){
33276                     cls += ' masonry-center-title';
33277                 }
33278
33279                 if(this.bgimage.length){
33280                     cls += ' masonry-bottom-title';
33281                 }
33282                 break;
33283         }
33284         
33285         if(this.cls){
33286             cls += ' ' + this.cls;
33287         }
33288         
33289         var cfg = {
33290             tag: (this.href.length) ? 'a' : 'div',
33291             cls: cls,
33292             cn: [
33293                 {
33294                     tag: 'div',
33295                     cls: 'masonry-brick-split-head',
33296                     cn: [
33297                         {
33298                             tag: 'div',
33299                             cls: 'masonry-brick-paragraph',
33300                             cn: []
33301                         }
33302                     ]
33303                 },
33304                 {
33305                     tag: 'div',
33306                     cls: 'masonry-brick-split-body',
33307                     cn: []
33308                 }
33309             ]
33310         };
33311         
33312         if(this.href.length){
33313             cfg.href = this.href;
33314         }
33315         
33316         if(this.title.length){
33317             cfg.cn[0].cn[0].cn.push({
33318                 tag: 'h4',
33319                 cls: 'masonry-brick-title',
33320                 html: this.title
33321             });
33322         }
33323         
33324         if(this.html.length){
33325             cfg.cn[1].cn.push({
33326                 tag: 'p',
33327                 cls: 'masonry-brick-text',
33328                 html: this.html
33329             });
33330         }
33331
33332         if(this.bgimage.length){
33333             cfg.cn[0].cn.push({
33334                 tag: 'img',
33335                 cls: 'masonry-brick-image-view',
33336                 src: this.bgimage
33337             });
33338         }
33339         
33340         if(this.videourl.length){
33341             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33342             // youtube support only?
33343             cfg.cn[0].cn.cn.push({
33344                 tag: 'iframe',
33345                 cls: 'masonry-brick-image-view',
33346                 src: vurl,
33347                 frameborder : 0,
33348                 allowfullscreen : true
33349             });
33350         }
33351         
33352         return cfg;
33353     },
33354     
33355     initEvents: function() 
33356     {
33357         switch (this.size) {
33358             case 'xs' :
33359                 this.x = 1;
33360                 this.y = 1;
33361                 break;
33362             case 'sm' :
33363                 this.x = 2;
33364                 this.y = 2;
33365                 break;
33366             case 'md' :
33367             case 'md-left' :
33368             case 'md-right' :
33369                 this.x = 3;
33370                 this.y = 3;
33371                 break;
33372             case 'tall' :
33373                 this.x = 2;
33374                 this.y = 3;
33375                 break;
33376             case 'wide' :
33377                 this.x = 3;
33378                 this.y = 2;
33379                 break;
33380             case 'wide-thin' :
33381                 this.x = 3;
33382                 this.y = 1;
33383                 break;
33384                         
33385             default :
33386                 break;
33387         }
33388         
33389         if(Roo.isTouch){
33390             this.el.on('touchstart', this.onTouchStart, this);
33391             this.el.on('touchmove', this.onTouchMove, this);
33392             this.el.on('touchend', this.onTouchEnd, this);
33393             this.el.on('contextmenu', this.onContextMenu, this);
33394         } else {
33395             this.el.on('mouseenter'  ,this.enter, this);
33396             this.el.on('mouseleave', this.leave, this);
33397             this.el.on('click', this.onClick, this);
33398         }
33399         
33400         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33401             this.parent().bricks.push(this);   
33402         }
33403         
33404     },
33405     
33406     onClick: function(e, el)
33407     {
33408         var time = this.endTimer - this.startTimer;
33409         // Roo.log(e.preventDefault());
33410         if(Roo.isTouch){
33411             if(time > 1000){
33412                 e.preventDefault();
33413                 return;
33414             }
33415         }
33416         
33417         if(!this.preventDefault){
33418             return;
33419         }
33420         
33421         e.preventDefault();
33422         
33423         if (this.activeClass != '') {
33424             this.selectBrick();
33425         }
33426         
33427         this.fireEvent('click', this, e);
33428     },
33429     
33430     enter: function(e, el)
33431     {
33432         e.preventDefault();
33433         
33434         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33435             return;
33436         }
33437         
33438         if(this.bgimage.length && this.html.length){
33439             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33440         }
33441     },
33442     
33443     leave: function(e, el)
33444     {
33445         e.preventDefault();
33446         
33447         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33448             return;
33449         }
33450         
33451         if(this.bgimage.length && this.html.length){
33452             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33453         }
33454     },
33455     
33456     onTouchStart: function(e, el)
33457     {
33458 //        e.preventDefault();
33459         
33460         this.touchmoved = false;
33461         
33462         if(!this.isFitContainer){
33463             return;
33464         }
33465         
33466         if(!this.bgimage.length || !this.html.length){
33467             return;
33468         }
33469         
33470         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33471         
33472         this.timer = new Date().getTime();
33473         
33474     },
33475     
33476     onTouchMove: function(e, el)
33477     {
33478         this.touchmoved = true;
33479     },
33480     
33481     onContextMenu : function(e,el)
33482     {
33483         e.preventDefault();
33484         e.stopPropagation();
33485         return false;
33486     },
33487     
33488     onTouchEnd: function(e, el)
33489     {
33490 //        e.preventDefault();
33491         
33492         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33493         
33494             this.leave(e,el);
33495             
33496             return;
33497         }
33498         
33499         if(!this.bgimage.length || !this.html.length){
33500             
33501             if(this.href.length){
33502                 window.location.href = this.href;
33503             }
33504             
33505             return;
33506         }
33507         
33508         if(!this.isFitContainer){
33509             return;
33510         }
33511         
33512         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33513         
33514         window.location.href = this.href;
33515     },
33516     
33517     //selection on single brick only
33518     selectBrick : function() {
33519         
33520         if (!this.parentId) {
33521             return;
33522         }
33523         
33524         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33525         var index = m.selectedBrick.indexOf(this.id);
33526         
33527         if ( index > -1) {
33528             m.selectedBrick.splice(index,1);
33529             this.el.removeClass(this.activeClass);
33530             return;
33531         }
33532         
33533         for(var i = 0; i < m.selectedBrick.length; i++) {
33534             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33535             b.el.removeClass(b.activeClass);
33536         }
33537         
33538         m.selectedBrick = [];
33539         
33540         m.selectedBrick.push(this.id);
33541         this.el.addClass(this.activeClass);
33542         return;
33543     },
33544     
33545     isSelected : function(){
33546         return this.el.hasClass(this.activeClass);
33547         
33548     }
33549 });
33550
33551 Roo.apply(Roo.bootstrap.MasonryBrick, {
33552     
33553     //groups: {},
33554     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33555      /**
33556     * register a Masonry Brick
33557     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33558     */
33559     
33560     register : function(brick)
33561     {
33562         //this.groups[brick.id] = brick;
33563         this.groups.add(brick.id, brick);
33564     },
33565     /**
33566     * fetch a  masonry brick based on the masonry brick ID
33567     * @param {string} the masonry brick to add
33568     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33569     */
33570     
33571     get: function(brick_id) 
33572     {
33573         // if (typeof(this.groups[brick_id]) == 'undefined') {
33574         //     return false;
33575         // }
33576         // return this.groups[brick_id] ;
33577         
33578         if(this.groups.key(brick_id)) {
33579             return this.groups.key(brick_id);
33580         }
33581         
33582         return false;
33583     }
33584     
33585     
33586     
33587 });
33588
33589  /*
33590  * - LGPL
33591  *
33592  * element
33593  * 
33594  */
33595
33596 /**
33597  * @class Roo.bootstrap.Brick
33598  * @extends Roo.bootstrap.Component
33599  * Bootstrap Brick class
33600  * 
33601  * @constructor
33602  * Create a new Brick
33603  * @param {Object} config The config object
33604  */
33605
33606 Roo.bootstrap.Brick = function(config){
33607     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33608     
33609     this.addEvents({
33610         // raw events
33611         /**
33612          * @event click
33613          * When a Brick is click
33614          * @param {Roo.bootstrap.Brick} this
33615          * @param {Roo.EventObject} e
33616          */
33617         "click" : true
33618     });
33619 };
33620
33621 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33622     
33623     /**
33624      * @cfg {String} title
33625      */   
33626     title : '',
33627     /**
33628      * @cfg {String} html
33629      */   
33630     html : '',
33631     /**
33632      * @cfg {String} bgimage
33633      */   
33634     bgimage : '',
33635     /**
33636      * @cfg {String} cls
33637      */   
33638     cls : '',
33639     /**
33640      * @cfg {String} href
33641      */   
33642     href : '',
33643     /**
33644      * @cfg {String} video
33645      */   
33646     video : '',
33647     /**
33648      * @cfg {Boolean} square
33649      */   
33650     square : true,
33651     
33652     getAutoCreate : function()
33653     {
33654         var cls = 'roo-brick';
33655         
33656         if(this.href.length){
33657             cls += ' roo-brick-link';
33658         }
33659         
33660         if(this.bgimage.length){
33661             cls += ' roo-brick-image';
33662         }
33663         
33664         if(!this.html.length && !this.bgimage.length){
33665             cls += ' roo-brick-center-title';
33666         }
33667         
33668         if(!this.html.length && this.bgimage.length){
33669             cls += ' roo-brick-bottom-title';
33670         }
33671         
33672         if(this.cls){
33673             cls += ' ' + this.cls;
33674         }
33675         
33676         var cfg = {
33677             tag: (this.href.length) ? 'a' : 'div',
33678             cls: cls,
33679             cn: [
33680                 {
33681                     tag: 'div',
33682                     cls: 'roo-brick-paragraph',
33683                     cn: []
33684                 }
33685             ]
33686         };
33687         
33688         if(this.href.length){
33689             cfg.href = this.href;
33690         }
33691         
33692         var cn = cfg.cn[0].cn;
33693         
33694         if(this.title.length){
33695             cn.push({
33696                 tag: 'h4',
33697                 cls: 'roo-brick-title',
33698                 html: this.title
33699             });
33700         }
33701         
33702         if(this.html.length){
33703             cn.push({
33704                 tag: 'p',
33705                 cls: 'roo-brick-text',
33706                 html: this.html
33707             });
33708         } else {
33709             cn.cls += ' hide';
33710         }
33711         
33712         if(this.bgimage.length){
33713             cfg.cn.push({
33714                 tag: 'img',
33715                 cls: 'roo-brick-image-view',
33716                 src: this.bgimage
33717             });
33718         }
33719         
33720         return cfg;
33721     },
33722     
33723     initEvents: function() 
33724     {
33725         if(this.title.length || this.html.length){
33726             this.el.on('mouseenter'  ,this.enter, this);
33727             this.el.on('mouseleave', this.leave, this);
33728         }
33729         
33730         Roo.EventManager.onWindowResize(this.resize, this); 
33731         
33732         if(this.bgimage.length){
33733             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33734             this.imageEl.on('load', this.onImageLoad, this);
33735             return;
33736         }
33737         
33738         this.resize();
33739     },
33740     
33741     onImageLoad : function()
33742     {
33743         this.resize();
33744     },
33745     
33746     resize : function()
33747     {
33748         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33749         
33750         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33751         
33752         if(this.bgimage.length){
33753             var image = this.el.select('.roo-brick-image-view', true).first();
33754             
33755             image.setWidth(paragraph.getWidth());
33756             
33757             if(this.square){
33758                 image.setHeight(paragraph.getWidth());
33759             }
33760             
33761             this.el.setHeight(image.getHeight());
33762             paragraph.setHeight(image.getHeight());
33763             
33764         }
33765         
33766     },
33767     
33768     enter: function(e, el)
33769     {
33770         e.preventDefault();
33771         
33772         if(this.bgimage.length){
33773             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33774             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33775         }
33776     },
33777     
33778     leave: function(e, el)
33779     {
33780         e.preventDefault();
33781         
33782         if(this.bgimage.length){
33783             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33784             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33785         }
33786     }
33787     
33788 });
33789
33790  
33791
33792  /*
33793  * - LGPL
33794  *
33795  * Number field 
33796  */
33797
33798 /**
33799  * @class Roo.bootstrap.NumberField
33800  * @extends Roo.bootstrap.Input
33801  * Bootstrap NumberField class
33802  * 
33803  * 
33804  * 
33805  * 
33806  * @constructor
33807  * Create a new NumberField
33808  * @param {Object} config The config object
33809  */
33810
33811 Roo.bootstrap.NumberField = function(config){
33812     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33813 };
33814
33815 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33816     
33817     /**
33818      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33819      */
33820     allowDecimals : true,
33821     /**
33822      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33823      */
33824     decimalSeparator : ".",
33825     /**
33826      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33827      */
33828     decimalPrecision : 2,
33829     /**
33830      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33831      */
33832     allowNegative : true,
33833     
33834     /**
33835      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33836      */
33837     allowZero: true,
33838     /**
33839      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33840      */
33841     minValue : Number.NEGATIVE_INFINITY,
33842     /**
33843      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33844      */
33845     maxValue : Number.MAX_VALUE,
33846     /**
33847      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33848      */
33849     minText : "The minimum value for this field is {0}",
33850     /**
33851      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33852      */
33853     maxText : "The maximum value for this field is {0}",
33854     /**
33855      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33856      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33857      */
33858     nanText : "{0} is not a valid number",
33859     /**
33860      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33861      */
33862     thousandsDelimiter : false,
33863     /**
33864      * @cfg {String} valueAlign alignment of value
33865      */
33866     valueAlign : "left",
33867
33868     getAutoCreate : function()
33869     {
33870         var hiddenInput = {
33871             tag: 'input',
33872             type: 'hidden',
33873             id: Roo.id(),
33874             cls: 'hidden-number-input'
33875         };
33876         
33877         if (this.name) {
33878             hiddenInput.name = this.name;
33879         }
33880         
33881         this.name = '';
33882         
33883         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33884         
33885         this.name = hiddenInput.name;
33886         
33887         if(cfg.cn.length > 0) {
33888             cfg.cn.push(hiddenInput);
33889         }
33890         
33891         return cfg;
33892     },
33893
33894     // private
33895     initEvents : function()
33896     {   
33897         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33898         
33899         var allowed = "0123456789";
33900         
33901         if(this.allowDecimals){
33902             allowed += this.decimalSeparator;
33903         }
33904         
33905         if(this.allowNegative){
33906             allowed += "-";
33907         }
33908         
33909         if(this.thousandsDelimiter) {
33910             allowed += ",";
33911         }
33912         
33913         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33914         
33915         var keyPress = function(e){
33916             
33917             var k = e.getKey();
33918             
33919             var c = e.getCharCode();
33920             
33921             if(
33922                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33923                     allowed.indexOf(String.fromCharCode(c)) === -1
33924             ){
33925                 e.stopEvent();
33926                 return;
33927             }
33928             
33929             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33930                 return;
33931             }
33932             
33933             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33934                 e.stopEvent();
33935             }
33936         };
33937         
33938         this.el.on("keypress", keyPress, this);
33939     },
33940     
33941     validateValue : function(value)
33942     {
33943         
33944         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33945             return false;
33946         }
33947         
33948         var num = this.parseValue(value);
33949         
33950         if(isNaN(num)){
33951             this.markInvalid(String.format(this.nanText, value));
33952             return false;
33953         }
33954         
33955         if(num < this.minValue){
33956             this.markInvalid(String.format(this.minText, this.minValue));
33957             return false;
33958         }
33959         
33960         if(num > this.maxValue){
33961             this.markInvalid(String.format(this.maxText, this.maxValue));
33962             return false;
33963         }
33964         
33965         return true;
33966     },
33967
33968     getValue : function()
33969     {
33970         var v = this.hiddenEl().getValue();
33971         
33972         return this.fixPrecision(this.parseValue(v));
33973     },
33974
33975     parseValue : function(value)
33976     {
33977         if(this.thousandsDelimiter) {
33978             value += "";
33979             r = new RegExp(",", "g");
33980             value = value.replace(r, "");
33981         }
33982         
33983         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33984         return isNaN(value) ? '' : value;
33985     },
33986
33987     fixPrecision : function(value)
33988     {
33989         if(this.thousandsDelimiter) {
33990             value += "";
33991             r = new RegExp(",", "g");
33992             value = value.replace(r, "");
33993         }
33994         
33995         var nan = isNaN(value);
33996         
33997         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33998             return nan ? '' : value;
33999         }
34000         return parseFloat(value).toFixed(this.decimalPrecision);
34001     },
34002
34003     setValue : function(v)
34004     {
34005         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
34006         
34007         this.value = v;
34008         
34009         if(this.rendered){
34010             
34011             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34012             
34013             this.inputEl().dom.value = (v == '') ? '' :
34014                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34015             
34016             if(!this.allowZero && v === '0') {
34017                 this.hiddenEl().dom.value = '';
34018                 this.inputEl().dom.value = '';
34019             }
34020             
34021             this.validate();
34022         }
34023     },
34024
34025     decimalPrecisionFcn : function(v)
34026     {
34027         return Math.floor(v);
34028     },
34029
34030     beforeBlur : function()
34031     {
34032         var v = this.parseValue(this.getRawValue());
34033         
34034         if(v || v === 0 || v === ''){
34035             this.setValue(v);
34036         }
34037     },
34038     
34039     hiddenEl : function()
34040     {
34041         return this.el.select('input.hidden-number-input',true).first();
34042     }
34043     
34044 });
34045
34046  
34047
34048 /*
34049 * Licence: LGPL
34050 */
34051
34052 /**
34053  * @class Roo.bootstrap.DocumentSlider
34054  * @extends Roo.bootstrap.Component
34055  * Bootstrap DocumentSlider class
34056  * 
34057  * @constructor
34058  * Create a new DocumentViewer
34059  * @param {Object} config The config object
34060  */
34061
34062 Roo.bootstrap.DocumentSlider = function(config){
34063     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
34064     
34065     this.files = [];
34066     
34067     this.addEvents({
34068         /**
34069          * @event initial
34070          * Fire after initEvent
34071          * @param {Roo.bootstrap.DocumentSlider} this
34072          */
34073         "initial" : true,
34074         /**
34075          * @event update
34076          * Fire after update
34077          * @param {Roo.bootstrap.DocumentSlider} this
34078          */
34079         "update" : true,
34080         /**
34081          * @event click
34082          * Fire after click
34083          * @param {Roo.bootstrap.DocumentSlider} this
34084          */
34085         "click" : true
34086     });
34087 };
34088
34089 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
34090     
34091     files : false,
34092     
34093     indicator : 0,
34094     
34095     getAutoCreate : function()
34096     {
34097         var cfg = {
34098             tag : 'div',
34099             cls : 'roo-document-slider',
34100             cn : [
34101                 {
34102                     tag : 'div',
34103                     cls : 'roo-document-slider-header',
34104                     cn : [
34105                         {
34106                             tag : 'div',
34107                             cls : 'roo-document-slider-header-title'
34108                         }
34109                     ]
34110                 },
34111                 {
34112                     tag : 'div',
34113                     cls : 'roo-document-slider-body',
34114                     cn : [
34115                         {
34116                             tag : 'div',
34117                             cls : 'roo-document-slider-prev',
34118                             cn : [
34119                                 {
34120                                     tag : 'i',
34121                                     cls : 'fa fa-chevron-left'
34122                                 }
34123                             ]
34124                         },
34125                         {
34126                             tag : 'div',
34127                             cls : 'roo-document-slider-thumb',
34128                             cn : [
34129                                 {
34130                                     tag : 'img',
34131                                     cls : 'roo-document-slider-image'
34132                                 }
34133                             ]
34134                         },
34135                         {
34136                             tag : 'div',
34137                             cls : 'roo-document-slider-next',
34138                             cn : [
34139                                 {
34140                                     tag : 'i',
34141                                     cls : 'fa fa-chevron-right'
34142                                 }
34143                             ]
34144                         }
34145                     ]
34146                 }
34147             ]
34148         };
34149         
34150         return cfg;
34151     },
34152     
34153     initEvents : function()
34154     {
34155         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34156         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34157         
34158         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34159         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34160         
34161         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34162         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34163         
34164         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34165         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34166         
34167         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34168         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34169         
34170         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34171         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34172         
34173         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34174         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34175         
34176         this.thumbEl.on('click', this.onClick, this);
34177         
34178         this.prevIndicator.on('click', this.prev, this);
34179         
34180         this.nextIndicator.on('click', this.next, this);
34181         
34182     },
34183     
34184     initial : function()
34185     {
34186         if(this.files.length){
34187             this.indicator = 1;
34188             this.update()
34189         }
34190         
34191         this.fireEvent('initial', this);
34192     },
34193     
34194     update : function()
34195     {
34196         this.imageEl.attr('src', this.files[this.indicator - 1]);
34197         
34198         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34199         
34200         this.prevIndicator.show();
34201         
34202         if(this.indicator == 1){
34203             this.prevIndicator.hide();
34204         }
34205         
34206         this.nextIndicator.show();
34207         
34208         if(this.indicator == this.files.length){
34209             this.nextIndicator.hide();
34210         }
34211         
34212         this.thumbEl.scrollTo('top');
34213         
34214         this.fireEvent('update', this);
34215     },
34216     
34217     onClick : function(e)
34218     {
34219         e.preventDefault();
34220         
34221         this.fireEvent('click', this);
34222     },
34223     
34224     prev : function(e)
34225     {
34226         e.preventDefault();
34227         
34228         this.indicator = Math.max(1, this.indicator - 1);
34229         
34230         this.update();
34231     },
34232     
34233     next : function(e)
34234     {
34235         e.preventDefault();
34236         
34237         this.indicator = Math.min(this.files.length, this.indicator + 1);
34238         
34239         this.update();
34240     }
34241 });
34242 /*
34243  * - LGPL
34244  *
34245  * RadioSet
34246  *
34247  *
34248  */
34249
34250 /**
34251  * @class Roo.bootstrap.RadioSet
34252  * @extends Roo.bootstrap.Input
34253  * Bootstrap RadioSet class
34254  * @cfg {String} indicatorpos (left|right) default left
34255  * @cfg {Boolean} inline (true|false) inline the element (default true)
34256  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34257  * @constructor
34258  * Create a new RadioSet
34259  * @param {Object} config The config object
34260  */
34261
34262 Roo.bootstrap.RadioSet = function(config){
34263     
34264     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34265     
34266     this.radioes = [];
34267     
34268     Roo.bootstrap.RadioSet.register(this);
34269     
34270     this.addEvents({
34271         /**
34272         * @event check
34273         * Fires when the element is checked or unchecked.
34274         * @param {Roo.bootstrap.RadioSet} this This radio
34275         * @param {Roo.bootstrap.Radio} item The checked item
34276         */
34277        check : true,
34278        /**
34279         * @event click
34280         * Fires when the element is click.
34281         * @param {Roo.bootstrap.RadioSet} this This radio set
34282         * @param {Roo.bootstrap.Radio} item The checked item
34283         * @param {Roo.EventObject} e The event object
34284         */
34285        click : true
34286     });
34287     
34288 };
34289
34290 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34291
34292     radioes : false,
34293     
34294     inline : true,
34295     
34296     weight : '',
34297     
34298     indicatorpos : 'left',
34299     
34300     getAutoCreate : function()
34301     {
34302         var label = {
34303             tag : 'label',
34304             cls : 'roo-radio-set-label',
34305             cn : [
34306                 {
34307                     tag : 'span',
34308                     html : this.fieldLabel
34309                 }
34310             ]
34311         };
34312         if (Roo.bootstrap.version == 3) {
34313             
34314             
34315             if(this.indicatorpos == 'left'){
34316                 label.cn.unshift({
34317                     tag : 'i',
34318                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34319                     tooltip : 'This field is required'
34320                 });
34321             } else {
34322                 label.cn.push({
34323                     tag : 'i',
34324                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34325                     tooltip : 'This field is required'
34326                 });
34327             }
34328         }
34329         var items = {
34330             tag : 'div',
34331             cls : 'roo-radio-set-items'
34332         };
34333         
34334         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34335         
34336         if (align === 'left' && this.fieldLabel.length) {
34337             
34338             items = {
34339                 cls : "roo-radio-set-right", 
34340                 cn: [
34341                     items
34342                 ]
34343             };
34344             
34345             if(this.labelWidth > 12){
34346                 label.style = "width: " + this.labelWidth + 'px';
34347             }
34348             
34349             if(this.labelWidth < 13 && this.labelmd == 0){
34350                 this.labelmd = this.labelWidth;
34351             }
34352             
34353             if(this.labellg > 0){
34354                 label.cls += ' col-lg-' + this.labellg;
34355                 items.cls += ' col-lg-' + (12 - this.labellg);
34356             }
34357             
34358             if(this.labelmd > 0){
34359                 label.cls += ' col-md-' + this.labelmd;
34360                 items.cls += ' col-md-' + (12 - this.labelmd);
34361             }
34362             
34363             if(this.labelsm > 0){
34364                 label.cls += ' col-sm-' + this.labelsm;
34365                 items.cls += ' col-sm-' + (12 - this.labelsm);
34366             }
34367             
34368             if(this.labelxs > 0){
34369                 label.cls += ' col-xs-' + this.labelxs;
34370                 items.cls += ' col-xs-' + (12 - this.labelxs);
34371             }
34372         }
34373         
34374         var cfg = {
34375             tag : 'div',
34376             cls : 'roo-radio-set',
34377             cn : [
34378                 {
34379                     tag : 'input',
34380                     cls : 'roo-radio-set-input',
34381                     type : 'hidden',
34382                     name : this.name,
34383                     value : this.value ? this.value :  ''
34384                 },
34385                 label,
34386                 items
34387             ]
34388         };
34389         
34390         if(this.weight.length){
34391             cfg.cls += ' roo-radio-' + this.weight;
34392         }
34393         
34394         if(this.inline) {
34395             cfg.cls += ' roo-radio-set-inline';
34396         }
34397         
34398         var settings=this;
34399         ['xs','sm','md','lg'].map(function(size){
34400             if (settings[size]) {
34401                 cfg.cls += ' col-' + size + '-' + settings[size];
34402             }
34403         });
34404         
34405         return cfg;
34406         
34407     },
34408
34409     initEvents : function()
34410     {
34411         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34412         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34413         
34414         if(!this.fieldLabel.length){
34415             this.labelEl.hide();
34416         }
34417         
34418         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34419         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34420         
34421         this.indicator = this.indicatorEl();
34422         
34423         if(this.indicator){
34424             this.indicator.addClass('invisible');
34425         }
34426         
34427         this.originalValue = this.getValue();
34428         
34429     },
34430     
34431     inputEl: function ()
34432     {
34433         return this.el.select('.roo-radio-set-input', true).first();
34434     },
34435     
34436     getChildContainer : function()
34437     {
34438         return this.itemsEl;
34439     },
34440     
34441     register : function(item)
34442     {
34443         this.radioes.push(item);
34444         
34445     },
34446     
34447     validate : function()
34448     {   
34449         if(this.getVisibilityEl().hasClass('hidden')){
34450             return true;
34451         }
34452         
34453         var valid = false;
34454         
34455         Roo.each(this.radioes, function(i){
34456             if(!i.checked){
34457                 return;
34458             }
34459             
34460             valid = true;
34461             return false;
34462         });
34463         
34464         if(this.allowBlank) {
34465             return true;
34466         }
34467         
34468         if(this.disabled || valid){
34469             this.markValid();
34470             return true;
34471         }
34472         
34473         this.markInvalid();
34474         return false;
34475         
34476     },
34477     
34478     markValid : function()
34479     {
34480         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34481             this.indicatorEl().removeClass('visible');
34482             this.indicatorEl().addClass('invisible');
34483         }
34484         
34485         
34486         if (Roo.bootstrap.version == 3) {
34487             this.el.removeClass([this.invalidClass, this.validClass]);
34488             this.el.addClass(this.validClass);
34489         } else {
34490             this.el.removeClass(['is-invalid','is-valid']);
34491             this.el.addClass(['is-valid']);
34492         }
34493         this.fireEvent('valid', this);
34494     },
34495     
34496     markInvalid : function(msg)
34497     {
34498         if(this.allowBlank || this.disabled){
34499             return;
34500         }
34501         
34502         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34503             this.indicatorEl().removeClass('invisible');
34504             this.indicatorEl().addClass('visible');
34505         }
34506         if (Roo.bootstrap.version == 3) {
34507             this.el.removeClass([this.invalidClass, this.validClass]);
34508             this.el.addClass(this.invalidClass);
34509         } else {
34510             this.el.removeClass(['is-invalid','is-valid']);
34511             this.el.addClass(['is-invalid']);
34512         }
34513         
34514         this.fireEvent('invalid', this, msg);
34515         
34516     },
34517     
34518     setValue : function(v, suppressEvent)
34519     {   
34520         if(this.value === v){
34521             return;
34522         }
34523         
34524         this.value = v;
34525         
34526         if(this.rendered){
34527             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34528         }
34529         
34530         Roo.each(this.radioes, function(i){
34531             i.checked = false;
34532             i.el.removeClass('checked');
34533         });
34534         
34535         Roo.each(this.radioes, function(i){
34536             
34537             if(i.value === v || i.value.toString() === v.toString()){
34538                 i.checked = true;
34539                 i.el.addClass('checked');
34540                 
34541                 if(suppressEvent !== true){
34542                     this.fireEvent('check', this, i);
34543                 }
34544                 
34545                 return false;
34546             }
34547             
34548         }, this);
34549         
34550         this.validate();
34551     },
34552     
34553     clearInvalid : function(){
34554         
34555         if(!this.el || this.preventMark){
34556             return;
34557         }
34558         
34559         this.el.removeClass([this.invalidClass]);
34560         
34561         this.fireEvent('valid', this);
34562     }
34563     
34564 });
34565
34566 Roo.apply(Roo.bootstrap.RadioSet, {
34567     
34568     groups: {},
34569     
34570     register : function(set)
34571     {
34572         this.groups[set.name] = set;
34573     },
34574     
34575     get: function(name) 
34576     {
34577         if (typeof(this.groups[name]) == 'undefined') {
34578             return false;
34579         }
34580         
34581         return this.groups[name] ;
34582     }
34583     
34584 });
34585 /*
34586  * Based on:
34587  * Ext JS Library 1.1.1
34588  * Copyright(c) 2006-2007, Ext JS, LLC.
34589  *
34590  * Originally Released Under LGPL - original licence link has changed is not relivant.
34591  *
34592  * Fork - LGPL
34593  * <script type="text/javascript">
34594  */
34595
34596
34597 /**
34598  * @class Roo.bootstrap.SplitBar
34599  * @extends Roo.util.Observable
34600  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34601  * <br><br>
34602  * Usage:
34603  * <pre><code>
34604 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34605                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34606 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34607 split.minSize = 100;
34608 split.maxSize = 600;
34609 split.animate = true;
34610 split.on('moved', splitterMoved);
34611 </code></pre>
34612  * @constructor
34613  * Create a new SplitBar
34614  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34615  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34616  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34617  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34618                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34619                         position of the SplitBar).
34620  */
34621 Roo.bootstrap.SplitBar = function(cfg){
34622     
34623     /** @private */
34624     
34625     //{
34626     //  dragElement : elm
34627     //  resizingElement: el,
34628         // optional..
34629     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34630     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34631         // existingProxy ???
34632     //}
34633     
34634     this.el = Roo.get(cfg.dragElement, true);
34635     this.el.dom.unselectable = "on";
34636     /** @private */
34637     this.resizingEl = Roo.get(cfg.resizingElement, true);
34638
34639     /**
34640      * @private
34641      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34642      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34643      * @type Number
34644      */
34645     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34646     
34647     /**
34648      * The minimum size of the resizing element. (Defaults to 0)
34649      * @type Number
34650      */
34651     this.minSize = 0;
34652     
34653     /**
34654      * The maximum size of the resizing element. (Defaults to 2000)
34655      * @type Number
34656      */
34657     this.maxSize = 2000;
34658     
34659     /**
34660      * Whether to animate the transition to the new size
34661      * @type Boolean
34662      */
34663     this.animate = false;
34664     
34665     /**
34666      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34667      * @type Boolean
34668      */
34669     this.useShim = false;
34670     
34671     /** @private */
34672     this.shim = null;
34673     
34674     if(!cfg.existingProxy){
34675         /** @private */
34676         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34677     }else{
34678         this.proxy = Roo.get(cfg.existingProxy).dom;
34679     }
34680     /** @private */
34681     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34682     
34683     /** @private */
34684     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34685     
34686     /** @private */
34687     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34688     
34689     /** @private */
34690     this.dragSpecs = {};
34691     
34692     /**
34693      * @private The adapter to use to positon and resize elements
34694      */
34695     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34696     this.adapter.init(this);
34697     
34698     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34699         /** @private */
34700         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34701         this.el.addClass("roo-splitbar-h");
34702     }else{
34703         /** @private */
34704         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34705         this.el.addClass("roo-splitbar-v");
34706     }
34707     
34708     this.addEvents({
34709         /**
34710          * @event resize
34711          * Fires when the splitter is moved (alias for {@link #event-moved})
34712          * @param {Roo.bootstrap.SplitBar} this
34713          * @param {Number} newSize the new width or height
34714          */
34715         "resize" : true,
34716         /**
34717          * @event moved
34718          * Fires when the splitter is moved
34719          * @param {Roo.bootstrap.SplitBar} this
34720          * @param {Number} newSize the new width or height
34721          */
34722         "moved" : true,
34723         /**
34724          * @event beforeresize
34725          * Fires before the splitter is dragged
34726          * @param {Roo.bootstrap.SplitBar} this
34727          */
34728         "beforeresize" : true,
34729
34730         "beforeapply" : true
34731     });
34732
34733     Roo.util.Observable.call(this);
34734 };
34735
34736 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34737     onStartProxyDrag : function(x, y){
34738         this.fireEvent("beforeresize", this);
34739         if(!this.overlay){
34740             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34741             o.unselectable();
34742             o.enableDisplayMode("block");
34743             // all splitbars share the same overlay
34744             Roo.bootstrap.SplitBar.prototype.overlay = o;
34745         }
34746         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34747         this.overlay.show();
34748         Roo.get(this.proxy).setDisplayed("block");
34749         var size = this.adapter.getElementSize(this);
34750         this.activeMinSize = this.getMinimumSize();;
34751         this.activeMaxSize = this.getMaximumSize();;
34752         var c1 = size - this.activeMinSize;
34753         var c2 = Math.max(this.activeMaxSize - size, 0);
34754         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34755             this.dd.resetConstraints();
34756             this.dd.setXConstraint(
34757                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34758                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34759             );
34760             this.dd.setYConstraint(0, 0);
34761         }else{
34762             this.dd.resetConstraints();
34763             this.dd.setXConstraint(0, 0);
34764             this.dd.setYConstraint(
34765                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34766                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34767             );
34768          }
34769         this.dragSpecs.startSize = size;
34770         this.dragSpecs.startPoint = [x, y];
34771         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34772     },
34773     
34774     /** 
34775      * @private Called after the drag operation by the DDProxy
34776      */
34777     onEndProxyDrag : function(e){
34778         Roo.get(this.proxy).setDisplayed(false);
34779         var endPoint = Roo.lib.Event.getXY(e);
34780         if(this.overlay){
34781             this.overlay.hide();
34782         }
34783         var newSize;
34784         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34785             newSize = this.dragSpecs.startSize + 
34786                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34787                     endPoint[0] - this.dragSpecs.startPoint[0] :
34788                     this.dragSpecs.startPoint[0] - endPoint[0]
34789                 );
34790         }else{
34791             newSize = this.dragSpecs.startSize + 
34792                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34793                     endPoint[1] - this.dragSpecs.startPoint[1] :
34794                     this.dragSpecs.startPoint[1] - endPoint[1]
34795                 );
34796         }
34797         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34798         if(newSize != this.dragSpecs.startSize){
34799             if(this.fireEvent('beforeapply', this, newSize) !== false){
34800                 this.adapter.setElementSize(this, newSize);
34801                 this.fireEvent("moved", this, newSize);
34802                 this.fireEvent("resize", this, newSize);
34803             }
34804         }
34805     },
34806     
34807     /**
34808      * Get the adapter this SplitBar uses
34809      * @return The adapter object
34810      */
34811     getAdapter : function(){
34812         return this.adapter;
34813     },
34814     
34815     /**
34816      * Set the adapter this SplitBar uses
34817      * @param {Object} adapter A SplitBar adapter object
34818      */
34819     setAdapter : function(adapter){
34820         this.adapter = adapter;
34821         this.adapter.init(this);
34822     },
34823     
34824     /**
34825      * Gets the minimum size for the resizing element
34826      * @return {Number} The minimum size
34827      */
34828     getMinimumSize : function(){
34829         return this.minSize;
34830     },
34831     
34832     /**
34833      * Sets the minimum size for the resizing element
34834      * @param {Number} minSize The minimum size
34835      */
34836     setMinimumSize : function(minSize){
34837         this.minSize = minSize;
34838     },
34839     
34840     /**
34841      * Gets the maximum size for the resizing element
34842      * @return {Number} The maximum size
34843      */
34844     getMaximumSize : function(){
34845         return this.maxSize;
34846     },
34847     
34848     /**
34849      * Sets the maximum size for the resizing element
34850      * @param {Number} maxSize The maximum size
34851      */
34852     setMaximumSize : function(maxSize){
34853         this.maxSize = maxSize;
34854     },
34855     
34856     /**
34857      * Sets the initialize size for the resizing element
34858      * @param {Number} size The initial size
34859      */
34860     setCurrentSize : function(size){
34861         var oldAnimate = this.animate;
34862         this.animate = false;
34863         this.adapter.setElementSize(this, size);
34864         this.animate = oldAnimate;
34865     },
34866     
34867     /**
34868      * Destroy this splitbar. 
34869      * @param {Boolean} removeEl True to remove the element
34870      */
34871     destroy : function(removeEl){
34872         if(this.shim){
34873             this.shim.remove();
34874         }
34875         this.dd.unreg();
34876         this.proxy.parentNode.removeChild(this.proxy);
34877         if(removeEl){
34878             this.el.remove();
34879         }
34880     }
34881 });
34882
34883 /**
34884  * @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.
34885  */
34886 Roo.bootstrap.SplitBar.createProxy = function(dir){
34887     var proxy = new Roo.Element(document.createElement("div"));
34888     proxy.unselectable();
34889     var cls = 'roo-splitbar-proxy';
34890     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34891     document.body.appendChild(proxy.dom);
34892     return proxy.dom;
34893 };
34894
34895 /** 
34896  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34897  * Default Adapter. It assumes the splitter and resizing element are not positioned
34898  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34899  */
34900 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34901 };
34902
34903 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34904     // do nothing for now
34905     init : function(s){
34906     
34907     },
34908     /**
34909      * Called before drag operations to get the current size of the resizing element. 
34910      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34911      */
34912      getElementSize : function(s){
34913         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34914             return s.resizingEl.getWidth();
34915         }else{
34916             return s.resizingEl.getHeight();
34917         }
34918     },
34919     
34920     /**
34921      * Called after drag operations to set the size of the resizing element.
34922      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34923      * @param {Number} newSize The new size to set
34924      * @param {Function} onComplete A function to be invoked when resizing is complete
34925      */
34926     setElementSize : function(s, newSize, onComplete){
34927         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34928             if(!s.animate){
34929                 s.resizingEl.setWidth(newSize);
34930                 if(onComplete){
34931                     onComplete(s, newSize);
34932                 }
34933             }else{
34934                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34935             }
34936         }else{
34937             
34938             if(!s.animate){
34939                 s.resizingEl.setHeight(newSize);
34940                 if(onComplete){
34941                     onComplete(s, newSize);
34942                 }
34943             }else{
34944                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34945             }
34946         }
34947     }
34948 };
34949
34950 /** 
34951  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34952  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34953  * Adapter that  moves the splitter element to align with the resized sizing element. 
34954  * Used with an absolute positioned SplitBar.
34955  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34956  * document.body, make sure you assign an id to the body element.
34957  */
34958 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34959     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34960     this.container = Roo.get(container);
34961 };
34962
34963 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34964     init : function(s){
34965         this.basic.init(s);
34966     },
34967     
34968     getElementSize : function(s){
34969         return this.basic.getElementSize(s);
34970     },
34971     
34972     setElementSize : function(s, newSize, onComplete){
34973         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34974     },
34975     
34976     moveSplitter : function(s){
34977         var yes = Roo.bootstrap.SplitBar;
34978         switch(s.placement){
34979             case yes.LEFT:
34980                 s.el.setX(s.resizingEl.getRight());
34981                 break;
34982             case yes.RIGHT:
34983                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34984                 break;
34985             case yes.TOP:
34986                 s.el.setY(s.resizingEl.getBottom());
34987                 break;
34988             case yes.BOTTOM:
34989                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34990                 break;
34991         }
34992     }
34993 };
34994
34995 /**
34996  * Orientation constant - Create a vertical SplitBar
34997  * @static
34998  * @type Number
34999  */
35000 Roo.bootstrap.SplitBar.VERTICAL = 1;
35001
35002 /**
35003  * Orientation constant - Create a horizontal SplitBar
35004  * @static
35005  * @type Number
35006  */
35007 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35008
35009 /**
35010  * Placement constant - The resizing element is to the left of the splitter element
35011  * @static
35012  * @type Number
35013  */
35014 Roo.bootstrap.SplitBar.LEFT = 1;
35015
35016 /**
35017  * Placement constant - The resizing element is to the right of the splitter element
35018  * @static
35019  * @type Number
35020  */
35021 Roo.bootstrap.SplitBar.RIGHT = 2;
35022
35023 /**
35024  * Placement constant - The resizing element is positioned above the splitter element
35025  * @static
35026  * @type Number
35027  */
35028 Roo.bootstrap.SplitBar.TOP = 3;
35029
35030 /**
35031  * Placement constant - The resizing element is positioned under splitter element
35032  * @static
35033  * @type Number
35034  */
35035 Roo.bootstrap.SplitBar.BOTTOM = 4;
35036 Roo.namespace("Roo.bootstrap.layout");/*
35037  * Based on:
35038  * Ext JS Library 1.1.1
35039  * Copyright(c) 2006-2007, Ext JS, LLC.
35040  *
35041  * Originally Released Under LGPL - original licence link has changed is not relivant.
35042  *
35043  * Fork - LGPL
35044  * <script type="text/javascript">
35045  */
35046
35047 /**
35048  * @class Roo.bootstrap.layout.Manager
35049  * @extends Roo.bootstrap.Component
35050  * Base class for layout managers.
35051  */
35052 Roo.bootstrap.layout.Manager = function(config)
35053 {
35054     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35055
35056
35057
35058
35059
35060     /** false to disable window resize monitoring @type Boolean */
35061     this.monitorWindowResize = true;
35062     this.regions = {};
35063     this.addEvents({
35064         /**
35065          * @event layout
35066          * Fires when a layout is performed.
35067          * @param {Roo.LayoutManager} this
35068          */
35069         "layout" : true,
35070         /**
35071          * @event regionresized
35072          * Fires when the user resizes a region.
35073          * @param {Roo.LayoutRegion} region The resized region
35074          * @param {Number} newSize The new size (width for east/west, height for north/south)
35075          */
35076         "regionresized" : true,
35077         /**
35078          * @event regioncollapsed
35079          * Fires when a region is collapsed.
35080          * @param {Roo.LayoutRegion} region The collapsed region
35081          */
35082         "regioncollapsed" : true,
35083         /**
35084          * @event regionexpanded
35085          * Fires when a region is expanded.
35086          * @param {Roo.LayoutRegion} region The expanded region
35087          */
35088         "regionexpanded" : true
35089     });
35090     this.updating = false;
35091
35092     if (config.el) {
35093         this.el = Roo.get(config.el);
35094         this.initEvents();
35095     }
35096
35097 };
35098
35099 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35100
35101
35102     regions : null,
35103
35104     monitorWindowResize : true,
35105
35106
35107     updating : false,
35108
35109
35110     onRender : function(ct, position)
35111     {
35112         if(!this.el){
35113             this.el = Roo.get(ct);
35114             this.initEvents();
35115         }
35116         //this.fireEvent('render',this);
35117     },
35118
35119
35120     initEvents: function()
35121     {
35122
35123
35124         // ie scrollbar fix
35125         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35126             document.body.scroll = "no";
35127         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35128             this.el.position('relative');
35129         }
35130         this.id = this.el.id;
35131         this.el.addClass("roo-layout-container");
35132         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35133         if(this.el.dom != document.body ) {
35134             this.el.on('resize', this.layout,this);
35135             this.el.on('show', this.layout,this);
35136         }
35137
35138     },
35139
35140     /**
35141      * Returns true if this layout is currently being updated
35142      * @return {Boolean}
35143      */
35144     isUpdating : function(){
35145         return this.updating;
35146     },
35147
35148     /**
35149      * Suspend the LayoutManager from doing auto-layouts while
35150      * making multiple add or remove calls
35151      */
35152     beginUpdate : function(){
35153         this.updating = true;
35154     },
35155
35156     /**
35157      * Restore auto-layouts and optionally disable the manager from performing a layout
35158      * @param {Boolean} noLayout true to disable a layout update
35159      */
35160     endUpdate : function(noLayout){
35161         this.updating = false;
35162         if(!noLayout){
35163             this.layout();
35164         }
35165     },
35166
35167     layout: function(){
35168         // abstract...
35169     },
35170
35171     onRegionResized : function(region, newSize){
35172         this.fireEvent("regionresized", region, newSize);
35173         this.layout();
35174     },
35175
35176     onRegionCollapsed : function(region){
35177         this.fireEvent("regioncollapsed", region);
35178     },
35179
35180     onRegionExpanded : function(region){
35181         this.fireEvent("regionexpanded", region);
35182     },
35183
35184     /**
35185      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35186      * performs box-model adjustments.
35187      * @return {Object} The size as an object {width: (the width), height: (the height)}
35188      */
35189     getViewSize : function()
35190     {
35191         var size;
35192         if(this.el.dom != document.body){
35193             size = this.el.getSize();
35194         }else{
35195             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35196         }
35197         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35198         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35199         return size;
35200     },
35201
35202     /**
35203      * Returns the Element this layout is bound to.
35204      * @return {Roo.Element}
35205      */
35206     getEl : function(){
35207         return this.el;
35208     },
35209
35210     /**
35211      * Returns the specified region.
35212      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35213      * @return {Roo.LayoutRegion}
35214      */
35215     getRegion : function(target){
35216         return this.regions[target.toLowerCase()];
35217     },
35218
35219     onWindowResize : function(){
35220         if(this.monitorWindowResize){
35221             this.layout();
35222         }
35223     }
35224 });
35225 /*
35226  * Based on:
35227  * Ext JS Library 1.1.1
35228  * Copyright(c) 2006-2007, Ext JS, LLC.
35229  *
35230  * Originally Released Under LGPL - original licence link has changed is not relivant.
35231  *
35232  * Fork - LGPL
35233  * <script type="text/javascript">
35234  */
35235 /**
35236  * @class Roo.bootstrap.layout.Border
35237  * @extends Roo.bootstrap.layout.Manager
35238  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35239  * please see: examples/bootstrap/nested.html<br><br>
35240  
35241 <b>The container the layout is rendered into can be either the body element or any other element.
35242 If it is not the body element, the container needs to either be an absolute positioned element,
35243 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35244 the container size if it is not the body element.</b>
35245
35246 * @constructor
35247 * Create a new Border
35248 * @param {Object} config Configuration options
35249  */
35250 Roo.bootstrap.layout.Border = function(config){
35251     config = config || {};
35252     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35253     
35254     
35255     
35256     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35257         if(config[region]){
35258             config[region].region = region;
35259             this.addRegion(config[region]);
35260         }
35261     },this);
35262     
35263 };
35264
35265 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35266
35267 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35268     
35269     parent : false, // this might point to a 'nest' or a ???
35270     
35271     /**
35272      * Creates and adds a new region if it doesn't already exist.
35273      * @param {String} target The target region key (north, south, east, west or center).
35274      * @param {Object} config The regions config object
35275      * @return {BorderLayoutRegion} The new region
35276      */
35277     addRegion : function(config)
35278     {
35279         if(!this.regions[config.region]){
35280             var r = this.factory(config);
35281             this.bindRegion(r);
35282         }
35283         return this.regions[config.region];
35284     },
35285
35286     // private (kinda)
35287     bindRegion : function(r){
35288         this.regions[r.config.region] = r;
35289         
35290         r.on("visibilitychange",    this.layout, this);
35291         r.on("paneladded",          this.layout, this);
35292         r.on("panelremoved",        this.layout, this);
35293         r.on("invalidated",         this.layout, this);
35294         r.on("resized",             this.onRegionResized, this);
35295         r.on("collapsed",           this.onRegionCollapsed, this);
35296         r.on("expanded",            this.onRegionExpanded, this);
35297     },
35298
35299     /**
35300      * Performs a layout update.
35301      */
35302     layout : function()
35303     {
35304         if(this.updating) {
35305             return;
35306         }
35307         
35308         // render all the rebions if they have not been done alreayd?
35309         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35310             if(this.regions[region] && !this.regions[region].bodyEl){
35311                 this.regions[region].onRender(this.el)
35312             }
35313         },this);
35314         
35315         var size = this.getViewSize();
35316         var w = size.width;
35317         var h = size.height;
35318         var centerW = w;
35319         var centerH = h;
35320         var centerY = 0;
35321         var centerX = 0;
35322         //var x = 0, y = 0;
35323
35324         var rs = this.regions;
35325         var north = rs["north"];
35326         var south = rs["south"]; 
35327         var west = rs["west"];
35328         var east = rs["east"];
35329         var center = rs["center"];
35330         //if(this.hideOnLayout){ // not supported anymore
35331             //c.el.setStyle("display", "none");
35332         //}
35333         if(north && north.isVisible()){
35334             var b = north.getBox();
35335             var m = north.getMargins();
35336             b.width = w - (m.left+m.right);
35337             b.x = m.left;
35338             b.y = m.top;
35339             centerY = b.height + b.y + m.bottom;
35340             centerH -= centerY;
35341             north.updateBox(this.safeBox(b));
35342         }
35343         if(south && south.isVisible()){
35344             var b = south.getBox();
35345             var m = south.getMargins();
35346             b.width = w - (m.left+m.right);
35347             b.x = m.left;
35348             var totalHeight = (b.height + m.top + m.bottom);
35349             b.y = h - totalHeight + m.top;
35350             centerH -= totalHeight;
35351             south.updateBox(this.safeBox(b));
35352         }
35353         if(west && west.isVisible()){
35354             var b = west.getBox();
35355             var m = west.getMargins();
35356             b.height = centerH - (m.top+m.bottom);
35357             b.x = m.left;
35358             b.y = centerY + m.top;
35359             var totalWidth = (b.width + m.left + m.right);
35360             centerX += totalWidth;
35361             centerW -= totalWidth;
35362             west.updateBox(this.safeBox(b));
35363         }
35364         if(east && east.isVisible()){
35365             var b = east.getBox();
35366             var m = east.getMargins();
35367             b.height = centerH - (m.top+m.bottom);
35368             var totalWidth = (b.width + m.left + m.right);
35369             b.x = w - totalWidth + m.left;
35370             b.y = centerY + m.top;
35371             centerW -= totalWidth;
35372             east.updateBox(this.safeBox(b));
35373         }
35374         if(center){
35375             var m = center.getMargins();
35376             var centerBox = {
35377                 x: centerX + m.left,
35378                 y: centerY + m.top,
35379                 width: centerW - (m.left+m.right),
35380                 height: centerH - (m.top+m.bottom)
35381             };
35382             //if(this.hideOnLayout){
35383                 //center.el.setStyle("display", "block");
35384             //}
35385             center.updateBox(this.safeBox(centerBox));
35386         }
35387         this.el.repaint();
35388         this.fireEvent("layout", this);
35389     },
35390
35391     // private
35392     safeBox : function(box){
35393         box.width = Math.max(0, box.width);
35394         box.height = Math.max(0, box.height);
35395         return box;
35396     },
35397
35398     /**
35399      * Adds a ContentPanel (or subclass) to this layout.
35400      * @param {String} target The target region key (north, south, east, west or center).
35401      * @param {Roo.ContentPanel} panel The panel to add
35402      * @return {Roo.ContentPanel} The added panel
35403      */
35404     add : function(target, panel){
35405          
35406         target = target.toLowerCase();
35407         return this.regions[target].add(panel);
35408     },
35409
35410     /**
35411      * Remove a ContentPanel (or subclass) to this layout.
35412      * @param {String} target The target region key (north, south, east, west or center).
35413      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35414      * @return {Roo.ContentPanel} The removed panel
35415      */
35416     remove : function(target, panel){
35417         target = target.toLowerCase();
35418         return this.regions[target].remove(panel);
35419     },
35420
35421     /**
35422      * Searches all regions for a panel with the specified id
35423      * @param {String} panelId
35424      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35425      */
35426     findPanel : function(panelId){
35427         var rs = this.regions;
35428         for(var target in rs){
35429             if(typeof rs[target] != "function"){
35430                 var p = rs[target].getPanel(panelId);
35431                 if(p){
35432                     return p;
35433                 }
35434             }
35435         }
35436         return null;
35437     },
35438
35439     /**
35440      * Searches all regions for a panel with the specified id and activates (shows) it.
35441      * @param {String/ContentPanel} panelId The panels id or the panel itself
35442      * @return {Roo.ContentPanel} The shown panel or null
35443      */
35444     showPanel : function(panelId) {
35445       var rs = this.regions;
35446       for(var target in rs){
35447          var r = rs[target];
35448          if(typeof r != "function"){
35449             if(r.hasPanel(panelId)){
35450                return r.showPanel(panelId);
35451             }
35452          }
35453       }
35454       return null;
35455    },
35456
35457    /**
35458      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35459      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35460      */
35461    /*
35462     restoreState : function(provider){
35463         if(!provider){
35464             provider = Roo.state.Manager;
35465         }
35466         var sm = new Roo.LayoutStateManager();
35467         sm.init(this, provider);
35468     },
35469 */
35470  
35471  
35472     /**
35473      * Adds a xtype elements to the layout.
35474      * <pre><code>
35475
35476 layout.addxtype({
35477        xtype : 'ContentPanel',
35478        region: 'west',
35479        items: [ .... ]
35480    }
35481 );
35482
35483 layout.addxtype({
35484         xtype : 'NestedLayoutPanel',
35485         region: 'west',
35486         layout: {
35487            center: { },
35488            west: { }   
35489         },
35490         items : [ ... list of content panels or nested layout panels.. ]
35491    }
35492 );
35493 </code></pre>
35494      * @param {Object} cfg Xtype definition of item to add.
35495      */
35496     addxtype : function(cfg)
35497     {
35498         // basically accepts a pannel...
35499         // can accept a layout region..!?!?
35500         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35501         
35502         
35503         // theory?  children can only be panels??
35504         
35505         //if (!cfg.xtype.match(/Panel$/)) {
35506         //    return false;
35507         //}
35508         var ret = false;
35509         
35510         if (typeof(cfg.region) == 'undefined') {
35511             Roo.log("Failed to add Panel, region was not set");
35512             Roo.log(cfg);
35513             return false;
35514         }
35515         var region = cfg.region;
35516         delete cfg.region;
35517         
35518           
35519         var xitems = [];
35520         if (cfg.items) {
35521             xitems = cfg.items;
35522             delete cfg.items;
35523         }
35524         var nb = false;
35525         
35526         if ( region == 'center') {
35527             Roo.log("Center: " + cfg.title);
35528         }
35529         
35530         
35531         switch(cfg.xtype) 
35532         {
35533             case 'Content':  // ContentPanel (el, cfg)
35534             case 'Scroll':  // ContentPanel (el, cfg)
35535             case 'View': 
35536                 cfg.autoCreate = cfg.autoCreate || true;
35537                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35538                 //} else {
35539                 //    var el = this.el.createChild();
35540                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35541                 //}
35542                 
35543                 this.add(region, ret);
35544                 break;
35545             
35546             /*
35547             case 'TreePanel': // our new panel!
35548                 cfg.el = this.el.createChild();
35549                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35550                 this.add(region, ret);
35551                 break;
35552             */
35553             
35554             case 'Nest': 
35555                 // create a new Layout (which is  a Border Layout...
35556                 
35557                 var clayout = cfg.layout;
35558                 clayout.el  = this.el.createChild();
35559                 clayout.items   = clayout.items  || [];
35560                 
35561                 delete cfg.layout;
35562                 
35563                 // replace this exitems with the clayout ones..
35564                 xitems = clayout.items;
35565                  
35566                 // force background off if it's in center...
35567                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35568                     cfg.background = false;
35569                 }
35570                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35571                 
35572                 
35573                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35574                 //console.log('adding nested layout panel '  + cfg.toSource());
35575                 this.add(region, ret);
35576                 nb = {}; /// find first...
35577                 break;
35578             
35579             case 'Grid':
35580                 
35581                 // needs grid and region
35582                 
35583                 //var el = this.getRegion(region).el.createChild();
35584                 /*
35585                  *var el = this.el.createChild();
35586                 // create the grid first...
35587                 cfg.grid.container = el;
35588                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35589                 */
35590                 
35591                 if (region == 'center' && this.active ) {
35592                     cfg.background = false;
35593                 }
35594                 
35595                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35596                 
35597                 this.add(region, ret);
35598                 /*
35599                 if (cfg.background) {
35600                     // render grid on panel activation (if panel background)
35601                     ret.on('activate', function(gp) {
35602                         if (!gp.grid.rendered) {
35603                     //        gp.grid.render(el);
35604                         }
35605                     });
35606                 } else {
35607                   //  cfg.grid.render(el);
35608                 }
35609                 */
35610                 break;
35611            
35612            
35613             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35614                 // it was the old xcomponent building that caused this before.
35615                 // espeically if border is the top element in the tree.
35616                 ret = this;
35617                 break; 
35618                 
35619                     
35620                 
35621                 
35622                 
35623             default:
35624                 /*
35625                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35626                     
35627                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35628                     this.add(region, ret);
35629                 } else {
35630                 */
35631                     Roo.log(cfg);
35632                     throw "Can not add '" + cfg.xtype + "' to Border";
35633                     return null;
35634              
35635                                 
35636              
35637         }
35638         this.beginUpdate();
35639         // add children..
35640         var region = '';
35641         var abn = {};
35642         Roo.each(xitems, function(i)  {
35643             region = nb && i.region ? i.region : false;
35644             
35645             var add = ret.addxtype(i);
35646            
35647             if (region) {
35648                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35649                 if (!i.background) {
35650                     abn[region] = nb[region] ;
35651                 }
35652             }
35653             
35654         });
35655         this.endUpdate();
35656
35657         // make the last non-background panel active..
35658         //if (nb) { Roo.log(abn); }
35659         if (nb) {
35660             
35661             for(var r in abn) {
35662                 region = this.getRegion(r);
35663                 if (region) {
35664                     // tried using nb[r], but it does not work..
35665                      
35666                     region.showPanel(abn[r]);
35667                    
35668                 }
35669             }
35670         }
35671         return ret;
35672         
35673     },
35674     
35675     
35676 // private
35677     factory : function(cfg)
35678     {
35679         
35680         var validRegions = Roo.bootstrap.layout.Border.regions;
35681
35682         var target = cfg.region;
35683         cfg.mgr = this;
35684         
35685         var r = Roo.bootstrap.layout;
35686         Roo.log(target);
35687         switch(target){
35688             case "north":
35689                 return new r.North(cfg);
35690             case "south":
35691                 return new r.South(cfg);
35692             case "east":
35693                 return new r.East(cfg);
35694             case "west":
35695                 return new r.West(cfg);
35696             case "center":
35697                 return new r.Center(cfg);
35698         }
35699         throw 'Layout region "'+target+'" not supported.';
35700     }
35701     
35702     
35703 });
35704  /*
35705  * Based on:
35706  * Ext JS Library 1.1.1
35707  * Copyright(c) 2006-2007, Ext JS, LLC.
35708  *
35709  * Originally Released Under LGPL - original licence link has changed is not relivant.
35710  *
35711  * Fork - LGPL
35712  * <script type="text/javascript">
35713  */
35714  
35715 /**
35716  * @class Roo.bootstrap.layout.Basic
35717  * @extends Roo.util.Observable
35718  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35719  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35720  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35721  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35722  * @cfg {string}   region  the region that it inhabits..
35723  * @cfg {bool}   skipConfig skip config?
35724  * 
35725
35726  */
35727 Roo.bootstrap.layout.Basic = function(config){
35728     
35729     this.mgr = config.mgr;
35730     
35731     this.position = config.region;
35732     
35733     var skipConfig = config.skipConfig;
35734     
35735     this.events = {
35736         /**
35737          * @scope Roo.BasicLayoutRegion
35738          */
35739         
35740         /**
35741          * @event beforeremove
35742          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35743          * @param {Roo.LayoutRegion} this
35744          * @param {Roo.ContentPanel} panel The panel
35745          * @param {Object} e The cancel event object
35746          */
35747         "beforeremove" : true,
35748         /**
35749          * @event invalidated
35750          * Fires when the layout for this region is changed.
35751          * @param {Roo.LayoutRegion} this
35752          */
35753         "invalidated" : true,
35754         /**
35755          * @event visibilitychange
35756          * Fires when this region is shown or hidden 
35757          * @param {Roo.LayoutRegion} this
35758          * @param {Boolean} visibility true or false
35759          */
35760         "visibilitychange" : true,
35761         /**
35762          * @event paneladded
35763          * Fires when a panel is added. 
35764          * @param {Roo.LayoutRegion} this
35765          * @param {Roo.ContentPanel} panel The panel
35766          */
35767         "paneladded" : true,
35768         /**
35769          * @event panelremoved
35770          * Fires when a panel is removed. 
35771          * @param {Roo.LayoutRegion} this
35772          * @param {Roo.ContentPanel} panel The panel
35773          */
35774         "panelremoved" : true,
35775         /**
35776          * @event beforecollapse
35777          * Fires when this region before collapse.
35778          * @param {Roo.LayoutRegion} this
35779          */
35780         "beforecollapse" : true,
35781         /**
35782          * @event collapsed
35783          * Fires when this region is collapsed.
35784          * @param {Roo.LayoutRegion} this
35785          */
35786         "collapsed" : true,
35787         /**
35788          * @event expanded
35789          * Fires when this region is expanded.
35790          * @param {Roo.LayoutRegion} this
35791          */
35792         "expanded" : true,
35793         /**
35794          * @event slideshow
35795          * Fires when this region is slid into view.
35796          * @param {Roo.LayoutRegion} this
35797          */
35798         "slideshow" : true,
35799         /**
35800          * @event slidehide
35801          * Fires when this region slides out of view. 
35802          * @param {Roo.LayoutRegion} this
35803          */
35804         "slidehide" : true,
35805         /**
35806          * @event panelactivated
35807          * Fires when a panel is activated. 
35808          * @param {Roo.LayoutRegion} this
35809          * @param {Roo.ContentPanel} panel The activated panel
35810          */
35811         "panelactivated" : true,
35812         /**
35813          * @event resized
35814          * Fires when the user resizes this region. 
35815          * @param {Roo.LayoutRegion} this
35816          * @param {Number} newSize The new size (width for east/west, height for north/south)
35817          */
35818         "resized" : true
35819     };
35820     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35821     this.panels = new Roo.util.MixedCollection();
35822     this.panels.getKey = this.getPanelId.createDelegate(this);
35823     this.box = null;
35824     this.activePanel = null;
35825     // ensure listeners are added...
35826     
35827     if (config.listeners || config.events) {
35828         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35829             listeners : config.listeners || {},
35830             events : config.events || {}
35831         });
35832     }
35833     
35834     if(skipConfig !== true){
35835         this.applyConfig(config);
35836     }
35837 };
35838
35839 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35840 {
35841     getPanelId : function(p){
35842         return p.getId();
35843     },
35844     
35845     applyConfig : function(config){
35846         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35847         this.config = config;
35848         
35849     },
35850     
35851     /**
35852      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35853      * the width, for horizontal (north, south) the height.
35854      * @param {Number} newSize The new width or height
35855      */
35856     resizeTo : function(newSize){
35857         var el = this.el ? this.el :
35858                  (this.activePanel ? this.activePanel.getEl() : null);
35859         if(el){
35860             switch(this.position){
35861                 case "east":
35862                 case "west":
35863                     el.setWidth(newSize);
35864                     this.fireEvent("resized", this, newSize);
35865                 break;
35866                 case "north":
35867                 case "south":
35868                     el.setHeight(newSize);
35869                     this.fireEvent("resized", this, newSize);
35870                 break;                
35871             }
35872         }
35873     },
35874     
35875     getBox : function(){
35876         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35877     },
35878     
35879     getMargins : function(){
35880         return this.margins;
35881     },
35882     
35883     updateBox : function(box){
35884         this.box = box;
35885         var el = this.activePanel.getEl();
35886         el.dom.style.left = box.x + "px";
35887         el.dom.style.top = box.y + "px";
35888         this.activePanel.setSize(box.width, box.height);
35889     },
35890     
35891     /**
35892      * Returns the container element for this region.
35893      * @return {Roo.Element}
35894      */
35895     getEl : function(){
35896         return this.activePanel;
35897     },
35898     
35899     /**
35900      * Returns true if this region is currently visible.
35901      * @return {Boolean}
35902      */
35903     isVisible : function(){
35904         return this.activePanel ? true : false;
35905     },
35906     
35907     setActivePanel : function(panel){
35908         panel = this.getPanel(panel);
35909         if(this.activePanel && this.activePanel != panel){
35910             this.activePanel.setActiveState(false);
35911             this.activePanel.getEl().setLeftTop(-10000,-10000);
35912         }
35913         this.activePanel = panel;
35914         panel.setActiveState(true);
35915         if(this.box){
35916             panel.setSize(this.box.width, this.box.height);
35917         }
35918         this.fireEvent("panelactivated", this, panel);
35919         this.fireEvent("invalidated");
35920     },
35921     
35922     /**
35923      * Show the specified panel.
35924      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35925      * @return {Roo.ContentPanel} The shown panel or null
35926      */
35927     showPanel : function(panel){
35928         panel = this.getPanel(panel);
35929         if(panel){
35930             this.setActivePanel(panel);
35931         }
35932         return panel;
35933     },
35934     
35935     /**
35936      * Get the active panel for this region.
35937      * @return {Roo.ContentPanel} The active panel or null
35938      */
35939     getActivePanel : function(){
35940         return this.activePanel;
35941     },
35942     
35943     /**
35944      * Add the passed ContentPanel(s)
35945      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35946      * @return {Roo.ContentPanel} The panel added (if only one was added)
35947      */
35948     add : function(panel){
35949         if(arguments.length > 1){
35950             for(var i = 0, len = arguments.length; i < len; i++) {
35951                 this.add(arguments[i]);
35952             }
35953             return null;
35954         }
35955         if(this.hasPanel(panel)){
35956             this.showPanel(panel);
35957             return panel;
35958         }
35959         var el = panel.getEl();
35960         if(el.dom.parentNode != this.mgr.el.dom){
35961             this.mgr.el.dom.appendChild(el.dom);
35962         }
35963         if(panel.setRegion){
35964             panel.setRegion(this);
35965         }
35966         this.panels.add(panel);
35967         el.setStyle("position", "absolute");
35968         if(!panel.background){
35969             this.setActivePanel(panel);
35970             if(this.config.initialSize && this.panels.getCount()==1){
35971                 this.resizeTo(this.config.initialSize);
35972             }
35973         }
35974         this.fireEvent("paneladded", this, panel);
35975         return panel;
35976     },
35977     
35978     /**
35979      * Returns true if the panel is in this region.
35980      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35981      * @return {Boolean}
35982      */
35983     hasPanel : function(panel){
35984         if(typeof panel == "object"){ // must be panel obj
35985             panel = panel.getId();
35986         }
35987         return this.getPanel(panel) ? true : false;
35988     },
35989     
35990     /**
35991      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35992      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35993      * @param {Boolean} preservePanel Overrides the config preservePanel option
35994      * @return {Roo.ContentPanel} The panel that was removed
35995      */
35996     remove : function(panel, preservePanel){
35997         panel = this.getPanel(panel);
35998         if(!panel){
35999             return null;
36000         }
36001         var e = {};
36002         this.fireEvent("beforeremove", this, panel, e);
36003         if(e.cancel === true){
36004             return null;
36005         }
36006         var panelId = panel.getId();
36007         this.panels.removeKey(panelId);
36008         return panel;
36009     },
36010     
36011     /**
36012      * Returns the panel specified or null if it's not in this region.
36013      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36014      * @return {Roo.ContentPanel}
36015      */
36016     getPanel : function(id){
36017         if(typeof id == "object"){ // must be panel obj
36018             return id;
36019         }
36020         return this.panels.get(id);
36021     },
36022     
36023     /**
36024      * Returns this regions position (north/south/east/west/center).
36025      * @return {String} 
36026      */
36027     getPosition: function(){
36028         return this.position;    
36029     }
36030 });/*
36031  * Based on:
36032  * Ext JS Library 1.1.1
36033  * Copyright(c) 2006-2007, Ext JS, LLC.
36034  *
36035  * Originally Released Under LGPL - original licence link has changed is not relivant.
36036  *
36037  * Fork - LGPL
36038  * <script type="text/javascript">
36039  */
36040  
36041 /**
36042  * @class Roo.bootstrap.layout.Region
36043  * @extends Roo.bootstrap.layout.Basic
36044  * This class represents a region in a layout manager.
36045  
36046  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36047  * @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})
36048  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
36049  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
36050  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
36051  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
36052  * @cfg {String}    title           The title for the region (overrides panel titles)
36053  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
36054  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36055  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
36056  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36057  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
36058  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36059  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
36060  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
36061  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
36062  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
36063
36064  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
36065  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
36066  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
36067  * @cfg {Number}    width           For East/West panels
36068  * @cfg {Number}    height          For North/South panels
36069  * @cfg {Boolean}   split           To show the splitter
36070  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
36071  * 
36072  * @cfg {string}   cls             Extra CSS classes to add to region
36073  * 
36074  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
36075  * @cfg {string}   region  the region that it inhabits..
36076  *
36077
36078  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
36079  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
36080
36081  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
36082  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
36083  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
36084  */
36085 Roo.bootstrap.layout.Region = function(config)
36086 {
36087     this.applyConfig(config);
36088
36089     var mgr = config.mgr;
36090     var pos = config.region;
36091     config.skipConfig = true;
36092     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36093     
36094     if (mgr.el) {
36095         this.onRender(mgr.el);   
36096     }
36097      
36098     this.visible = true;
36099     this.collapsed = false;
36100     this.unrendered_panels = [];
36101 };
36102
36103 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36104
36105     position: '', // set by wrapper (eg. north/south etc..)
36106     unrendered_panels : null,  // unrendered panels.
36107     
36108     tabPosition : false,
36109     
36110     mgr: false, // points to 'Border'
36111     
36112     
36113     createBody : function(){
36114         /** This region's body element 
36115         * @type Roo.Element */
36116         this.bodyEl = this.el.createChild({
36117                 tag: "div",
36118                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36119         });
36120     },
36121
36122     onRender: function(ctr, pos)
36123     {
36124         var dh = Roo.DomHelper;
36125         /** This region's container element 
36126         * @type Roo.Element */
36127         this.el = dh.append(ctr.dom, {
36128                 tag: "div",
36129                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36130             }, true);
36131         /** This region's title element 
36132         * @type Roo.Element */
36133     
36134         this.titleEl = dh.append(this.el.dom,  {
36135                 tag: "div",
36136                 unselectable: "on",
36137                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36138                 children:[
36139                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
36140                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36141                 ]
36142             }, true);
36143         
36144         this.titleEl.enableDisplayMode();
36145         /** This region's title text element 
36146         * @type HTMLElement */
36147         this.titleTextEl = this.titleEl.dom.firstChild;
36148         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36149         /*
36150         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36151         this.closeBtn.enableDisplayMode();
36152         this.closeBtn.on("click", this.closeClicked, this);
36153         this.closeBtn.hide();
36154     */
36155         this.createBody(this.config);
36156         if(this.config.hideWhenEmpty){
36157             this.hide();
36158             this.on("paneladded", this.validateVisibility, this);
36159             this.on("panelremoved", this.validateVisibility, this);
36160         }
36161         if(this.autoScroll){
36162             this.bodyEl.setStyle("overflow", "auto");
36163         }else{
36164             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36165         }
36166         //if(c.titlebar !== false){
36167             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36168                 this.titleEl.hide();
36169             }else{
36170                 this.titleEl.show();
36171                 if(this.config.title){
36172                     this.titleTextEl.innerHTML = this.config.title;
36173                 }
36174             }
36175         //}
36176         if(this.config.collapsed){
36177             this.collapse(true);
36178         }
36179         if(this.config.hidden){
36180             this.hide();
36181         }
36182         
36183         if (this.unrendered_panels && this.unrendered_panels.length) {
36184             for (var i =0;i< this.unrendered_panels.length; i++) {
36185                 this.add(this.unrendered_panels[i]);
36186             }
36187             this.unrendered_panels = null;
36188             
36189         }
36190         
36191     },
36192     
36193     applyConfig : function(c)
36194     {
36195         /*
36196          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36197             var dh = Roo.DomHelper;
36198             if(c.titlebar !== false){
36199                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36200                 this.collapseBtn.on("click", this.collapse, this);
36201                 this.collapseBtn.enableDisplayMode();
36202                 /*
36203                 if(c.showPin === true || this.showPin){
36204                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36205                     this.stickBtn.enableDisplayMode();
36206                     this.stickBtn.on("click", this.expand, this);
36207                     this.stickBtn.hide();
36208                 }
36209                 
36210             }
36211             */
36212             /** This region's collapsed element
36213             * @type Roo.Element */
36214             /*
36215              *
36216             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36217                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36218             ]}, true);
36219             
36220             if(c.floatable !== false){
36221                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36222                this.collapsedEl.on("click", this.collapseClick, this);
36223             }
36224
36225             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36226                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36227                    id: "message", unselectable: "on", style:{"float":"left"}});
36228                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36229              }
36230             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36231             this.expandBtn.on("click", this.expand, this);
36232             
36233         }
36234         
36235         if(this.collapseBtn){
36236             this.collapseBtn.setVisible(c.collapsible == true);
36237         }
36238         
36239         this.cmargins = c.cmargins || this.cmargins ||
36240                          (this.position == "west" || this.position == "east" ?
36241                              {top: 0, left: 2, right:2, bottom: 0} :
36242                              {top: 2, left: 0, right:0, bottom: 2});
36243         */
36244         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36245         
36246         
36247         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36248         
36249         this.autoScroll = c.autoScroll || false;
36250         
36251         
36252        
36253         
36254         this.duration = c.duration || .30;
36255         this.slideDuration = c.slideDuration || .45;
36256         this.config = c;
36257        
36258     },
36259     /**
36260      * Returns true if this region is currently visible.
36261      * @return {Boolean}
36262      */
36263     isVisible : function(){
36264         return this.visible;
36265     },
36266
36267     /**
36268      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36269      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36270      */
36271     //setCollapsedTitle : function(title){
36272     //    title = title || "&#160;";
36273      //   if(this.collapsedTitleTextEl){
36274       //      this.collapsedTitleTextEl.innerHTML = title;
36275        // }
36276     //},
36277
36278     getBox : function(){
36279         var b;
36280       //  if(!this.collapsed){
36281             b = this.el.getBox(false, true);
36282        // }else{
36283           //  b = this.collapsedEl.getBox(false, true);
36284         //}
36285         return b;
36286     },
36287
36288     getMargins : function(){
36289         return this.margins;
36290         //return this.collapsed ? this.cmargins : this.margins;
36291     },
36292 /*
36293     highlight : function(){
36294         this.el.addClass("x-layout-panel-dragover");
36295     },
36296
36297     unhighlight : function(){
36298         this.el.removeClass("x-layout-panel-dragover");
36299     },
36300 */
36301     updateBox : function(box)
36302     {
36303         if (!this.bodyEl) {
36304             return; // not rendered yet..
36305         }
36306         
36307         this.box = box;
36308         if(!this.collapsed){
36309             this.el.dom.style.left = box.x + "px";
36310             this.el.dom.style.top = box.y + "px";
36311             this.updateBody(box.width, box.height);
36312         }else{
36313             this.collapsedEl.dom.style.left = box.x + "px";
36314             this.collapsedEl.dom.style.top = box.y + "px";
36315             this.collapsedEl.setSize(box.width, box.height);
36316         }
36317         if(this.tabs){
36318             this.tabs.autoSizeTabs();
36319         }
36320     },
36321
36322     updateBody : function(w, h)
36323     {
36324         if(w !== null){
36325             this.el.setWidth(w);
36326             w -= this.el.getBorderWidth("rl");
36327             if(this.config.adjustments){
36328                 w += this.config.adjustments[0];
36329             }
36330         }
36331         if(h !== null && h > 0){
36332             this.el.setHeight(h);
36333             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36334             h -= this.el.getBorderWidth("tb");
36335             if(this.config.adjustments){
36336                 h += this.config.adjustments[1];
36337             }
36338             this.bodyEl.setHeight(h);
36339             if(this.tabs){
36340                 h = this.tabs.syncHeight(h);
36341             }
36342         }
36343         if(this.panelSize){
36344             w = w !== null ? w : this.panelSize.width;
36345             h = h !== null ? h : this.panelSize.height;
36346         }
36347         if(this.activePanel){
36348             var el = this.activePanel.getEl();
36349             w = w !== null ? w : el.getWidth();
36350             h = h !== null ? h : el.getHeight();
36351             this.panelSize = {width: w, height: h};
36352             this.activePanel.setSize(w, h);
36353         }
36354         if(Roo.isIE && this.tabs){
36355             this.tabs.el.repaint();
36356         }
36357     },
36358
36359     /**
36360      * Returns the container element for this region.
36361      * @return {Roo.Element}
36362      */
36363     getEl : function(){
36364         return this.el;
36365     },
36366
36367     /**
36368      * Hides this region.
36369      */
36370     hide : function(){
36371         //if(!this.collapsed){
36372             this.el.dom.style.left = "-2000px";
36373             this.el.hide();
36374         //}else{
36375          //   this.collapsedEl.dom.style.left = "-2000px";
36376          //   this.collapsedEl.hide();
36377        // }
36378         this.visible = false;
36379         this.fireEvent("visibilitychange", this, false);
36380     },
36381
36382     /**
36383      * Shows this region if it was previously hidden.
36384      */
36385     show : function(){
36386         //if(!this.collapsed){
36387             this.el.show();
36388         //}else{
36389         //    this.collapsedEl.show();
36390        // }
36391         this.visible = true;
36392         this.fireEvent("visibilitychange", this, true);
36393     },
36394 /*
36395     closeClicked : function(){
36396         if(this.activePanel){
36397             this.remove(this.activePanel);
36398         }
36399     },
36400
36401     collapseClick : function(e){
36402         if(this.isSlid){
36403            e.stopPropagation();
36404            this.slideIn();
36405         }else{
36406            e.stopPropagation();
36407            this.slideOut();
36408         }
36409     },
36410 */
36411     /**
36412      * Collapses this region.
36413      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36414      */
36415     /*
36416     collapse : function(skipAnim, skipCheck = false){
36417         if(this.collapsed) {
36418             return;
36419         }
36420         
36421         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36422             
36423             this.collapsed = true;
36424             if(this.split){
36425                 this.split.el.hide();
36426             }
36427             if(this.config.animate && skipAnim !== true){
36428                 this.fireEvent("invalidated", this);
36429                 this.animateCollapse();
36430             }else{
36431                 this.el.setLocation(-20000,-20000);
36432                 this.el.hide();
36433                 this.collapsedEl.show();
36434                 this.fireEvent("collapsed", this);
36435                 this.fireEvent("invalidated", this);
36436             }
36437         }
36438         
36439     },
36440 */
36441     animateCollapse : function(){
36442         // overridden
36443     },
36444
36445     /**
36446      * Expands this region if it was previously collapsed.
36447      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36448      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36449      */
36450     /*
36451     expand : function(e, skipAnim){
36452         if(e) {
36453             e.stopPropagation();
36454         }
36455         if(!this.collapsed || this.el.hasActiveFx()) {
36456             return;
36457         }
36458         if(this.isSlid){
36459             this.afterSlideIn();
36460             skipAnim = true;
36461         }
36462         this.collapsed = false;
36463         if(this.config.animate && skipAnim !== true){
36464             this.animateExpand();
36465         }else{
36466             this.el.show();
36467             if(this.split){
36468                 this.split.el.show();
36469             }
36470             this.collapsedEl.setLocation(-2000,-2000);
36471             this.collapsedEl.hide();
36472             this.fireEvent("invalidated", this);
36473             this.fireEvent("expanded", this);
36474         }
36475     },
36476 */
36477     animateExpand : function(){
36478         // overridden
36479     },
36480
36481     initTabs : function()
36482     {
36483         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36484         
36485         var ts = new Roo.bootstrap.panel.Tabs({
36486             el: this.bodyEl.dom,
36487             region : this,
36488             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36489             disableTooltips: this.config.disableTabTips,
36490             toolbar : this.config.toolbar
36491         });
36492         
36493         if(this.config.hideTabs){
36494             ts.stripWrap.setDisplayed(false);
36495         }
36496         this.tabs = ts;
36497         ts.resizeTabs = this.config.resizeTabs === true;
36498         ts.minTabWidth = this.config.minTabWidth || 40;
36499         ts.maxTabWidth = this.config.maxTabWidth || 250;
36500         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36501         ts.monitorResize = false;
36502         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36503         ts.bodyEl.addClass('roo-layout-tabs-body');
36504         this.panels.each(this.initPanelAsTab, this);
36505     },
36506
36507     initPanelAsTab : function(panel){
36508         var ti = this.tabs.addTab(
36509             panel.getEl().id,
36510             panel.getTitle(),
36511             null,
36512             this.config.closeOnTab && panel.isClosable(),
36513             panel.tpl
36514         );
36515         if(panel.tabTip !== undefined){
36516             ti.setTooltip(panel.tabTip);
36517         }
36518         ti.on("activate", function(){
36519               this.setActivePanel(panel);
36520         }, this);
36521         
36522         if(this.config.closeOnTab){
36523             ti.on("beforeclose", function(t, e){
36524                 e.cancel = true;
36525                 this.remove(panel);
36526             }, this);
36527         }
36528         
36529         panel.tabItem = ti;
36530         
36531         return ti;
36532     },
36533
36534     updatePanelTitle : function(panel, title)
36535     {
36536         if(this.activePanel == panel){
36537             this.updateTitle(title);
36538         }
36539         if(this.tabs){
36540             var ti = this.tabs.getTab(panel.getEl().id);
36541             ti.setText(title);
36542             if(panel.tabTip !== undefined){
36543                 ti.setTooltip(panel.tabTip);
36544             }
36545         }
36546     },
36547
36548     updateTitle : function(title){
36549         if(this.titleTextEl && !this.config.title){
36550             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36551         }
36552     },
36553
36554     setActivePanel : function(panel)
36555     {
36556         panel = this.getPanel(panel);
36557         if(this.activePanel && this.activePanel != panel){
36558             if(this.activePanel.setActiveState(false) === false){
36559                 return;
36560             }
36561         }
36562         this.activePanel = panel;
36563         panel.setActiveState(true);
36564         if(this.panelSize){
36565             panel.setSize(this.panelSize.width, this.panelSize.height);
36566         }
36567         if(this.closeBtn){
36568             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36569         }
36570         this.updateTitle(panel.getTitle());
36571         if(this.tabs){
36572             this.fireEvent("invalidated", this);
36573         }
36574         this.fireEvent("panelactivated", this, panel);
36575     },
36576
36577     /**
36578      * Shows the specified panel.
36579      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36580      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36581      */
36582     showPanel : function(panel)
36583     {
36584         panel = this.getPanel(panel);
36585         if(panel){
36586             if(this.tabs){
36587                 var tab = this.tabs.getTab(panel.getEl().id);
36588                 if(tab.isHidden()){
36589                     this.tabs.unhideTab(tab.id);
36590                 }
36591                 tab.activate();
36592             }else{
36593                 this.setActivePanel(panel);
36594             }
36595         }
36596         return panel;
36597     },
36598
36599     /**
36600      * Get the active panel for this region.
36601      * @return {Roo.ContentPanel} The active panel or null
36602      */
36603     getActivePanel : function(){
36604         return this.activePanel;
36605     },
36606
36607     validateVisibility : function(){
36608         if(this.panels.getCount() < 1){
36609             this.updateTitle("&#160;");
36610             this.closeBtn.hide();
36611             this.hide();
36612         }else{
36613             if(!this.isVisible()){
36614                 this.show();
36615             }
36616         }
36617     },
36618
36619     /**
36620      * Adds the passed ContentPanel(s) to this region.
36621      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36622      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36623      */
36624     add : function(panel)
36625     {
36626         if(arguments.length > 1){
36627             for(var i = 0, len = arguments.length; i < len; i++) {
36628                 this.add(arguments[i]);
36629             }
36630             return null;
36631         }
36632         
36633         // if we have not been rendered yet, then we can not really do much of this..
36634         if (!this.bodyEl) {
36635             this.unrendered_panels.push(panel);
36636             return panel;
36637         }
36638         
36639         
36640         
36641         
36642         if(this.hasPanel(panel)){
36643             this.showPanel(panel);
36644             return panel;
36645         }
36646         panel.setRegion(this);
36647         this.panels.add(panel);
36648        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36649             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36650             // and hide them... ???
36651             this.bodyEl.dom.appendChild(panel.getEl().dom);
36652             if(panel.background !== true){
36653                 this.setActivePanel(panel);
36654             }
36655             this.fireEvent("paneladded", this, panel);
36656             return panel;
36657         }
36658         */
36659         if(!this.tabs){
36660             this.initTabs();
36661         }else{
36662             this.initPanelAsTab(panel);
36663         }
36664         
36665         
36666         if(panel.background !== true){
36667             this.tabs.activate(panel.getEl().id);
36668         }
36669         this.fireEvent("paneladded", this, panel);
36670         return panel;
36671     },
36672
36673     /**
36674      * Hides the tab for the specified panel.
36675      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36676      */
36677     hidePanel : function(panel){
36678         if(this.tabs && (panel = this.getPanel(panel))){
36679             this.tabs.hideTab(panel.getEl().id);
36680         }
36681     },
36682
36683     /**
36684      * Unhides the tab for a previously hidden panel.
36685      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36686      */
36687     unhidePanel : function(panel){
36688         if(this.tabs && (panel = this.getPanel(panel))){
36689             this.tabs.unhideTab(panel.getEl().id);
36690         }
36691     },
36692
36693     clearPanels : function(){
36694         while(this.panels.getCount() > 0){
36695              this.remove(this.panels.first());
36696         }
36697     },
36698
36699     /**
36700      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36701      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36702      * @param {Boolean} preservePanel Overrides the config preservePanel option
36703      * @return {Roo.ContentPanel} The panel that was removed
36704      */
36705     remove : function(panel, preservePanel)
36706     {
36707         panel = this.getPanel(panel);
36708         if(!panel){
36709             return null;
36710         }
36711         var e = {};
36712         this.fireEvent("beforeremove", this, panel, e);
36713         if(e.cancel === true){
36714             return null;
36715         }
36716         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36717         var panelId = panel.getId();
36718         this.panels.removeKey(panelId);
36719         if(preservePanel){
36720             document.body.appendChild(panel.getEl().dom);
36721         }
36722         if(this.tabs){
36723             this.tabs.removeTab(panel.getEl().id);
36724         }else if (!preservePanel){
36725             this.bodyEl.dom.removeChild(panel.getEl().dom);
36726         }
36727         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36728             var p = this.panels.first();
36729             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36730             tempEl.appendChild(p.getEl().dom);
36731             this.bodyEl.update("");
36732             this.bodyEl.dom.appendChild(p.getEl().dom);
36733             tempEl = null;
36734             this.updateTitle(p.getTitle());
36735             this.tabs = null;
36736             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36737             this.setActivePanel(p);
36738         }
36739         panel.setRegion(null);
36740         if(this.activePanel == panel){
36741             this.activePanel = null;
36742         }
36743         if(this.config.autoDestroy !== false && preservePanel !== true){
36744             try{panel.destroy();}catch(e){}
36745         }
36746         this.fireEvent("panelremoved", this, panel);
36747         return panel;
36748     },
36749
36750     /**
36751      * Returns the TabPanel component used by this region
36752      * @return {Roo.TabPanel}
36753      */
36754     getTabs : function(){
36755         return this.tabs;
36756     },
36757
36758     createTool : function(parentEl, className){
36759         var btn = Roo.DomHelper.append(parentEl, {
36760             tag: "div",
36761             cls: "x-layout-tools-button",
36762             children: [ {
36763                 tag: "div",
36764                 cls: "roo-layout-tools-button-inner " + className,
36765                 html: "&#160;"
36766             }]
36767         }, true);
36768         btn.addClassOnOver("roo-layout-tools-button-over");
36769         return btn;
36770     }
36771 });/*
36772  * Based on:
36773  * Ext JS Library 1.1.1
36774  * Copyright(c) 2006-2007, Ext JS, LLC.
36775  *
36776  * Originally Released Under LGPL - original licence link has changed is not relivant.
36777  *
36778  * Fork - LGPL
36779  * <script type="text/javascript">
36780  */
36781  
36782
36783
36784 /**
36785  * @class Roo.SplitLayoutRegion
36786  * @extends Roo.LayoutRegion
36787  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36788  */
36789 Roo.bootstrap.layout.Split = function(config){
36790     this.cursor = config.cursor;
36791     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36792 };
36793
36794 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36795 {
36796     splitTip : "Drag to resize.",
36797     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36798     useSplitTips : false,
36799
36800     applyConfig : function(config){
36801         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36802     },
36803     
36804     onRender : function(ctr,pos) {
36805         
36806         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36807         if(!this.config.split){
36808             return;
36809         }
36810         if(!this.split){
36811             
36812             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36813                             tag: "div",
36814                             id: this.el.id + "-split",
36815                             cls: "roo-layout-split roo-layout-split-"+this.position,
36816                             html: "&#160;"
36817             });
36818             /** The SplitBar for this region 
36819             * @type Roo.SplitBar */
36820             // does not exist yet...
36821             Roo.log([this.position, this.orientation]);
36822             
36823             this.split = new Roo.bootstrap.SplitBar({
36824                 dragElement : splitEl,
36825                 resizingElement: this.el,
36826                 orientation : this.orientation
36827             });
36828             
36829             this.split.on("moved", this.onSplitMove, this);
36830             this.split.useShim = this.config.useShim === true;
36831             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36832             if(this.useSplitTips){
36833                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36834             }
36835             //if(config.collapsible){
36836             //    this.split.el.on("dblclick", this.collapse,  this);
36837             //}
36838         }
36839         if(typeof this.config.minSize != "undefined"){
36840             this.split.minSize = this.config.minSize;
36841         }
36842         if(typeof this.config.maxSize != "undefined"){
36843             this.split.maxSize = this.config.maxSize;
36844         }
36845         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36846             this.hideSplitter();
36847         }
36848         
36849     },
36850
36851     getHMaxSize : function(){
36852          var cmax = this.config.maxSize || 10000;
36853          var center = this.mgr.getRegion("center");
36854          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36855     },
36856
36857     getVMaxSize : function(){
36858          var cmax = this.config.maxSize || 10000;
36859          var center = this.mgr.getRegion("center");
36860          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36861     },
36862
36863     onSplitMove : function(split, newSize){
36864         this.fireEvent("resized", this, newSize);
36865     },
36866     
36867     /** 
36868      * Returns the {@link Roo.SplitBar} for this region.
36869      * @return {Roo.SplitBar}
36870      */
36871     getSplitBar : function(){
36872         return this.split;
36873     },
36874     
36875     hide : function(){
36876         this.hideSplitter();
36877         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36878     },
36879
36880     hideSplitter : function(){
36881         if(this.split){
36882             this.split.el.setLocation(-2000,-2000);
36883             this.split.el.hide();
36884         }
36885     },
36886
36887     show : function(){
36888         if(this.split){
36889             this.split.el.show();
36890         }
36891         Roo.bootstrap.layout.Split.superclass.show.call(this);
36892     },
36893     
36894     beforeSlide: function(){
36895         if(Roo.isGecko){// firefox overflow auto bug workaround
36896             this.bodyEl.clip();
36897             if(this.tabs) {
36898                 this.tabs.bodyEl.clip();
36899             }
36900             if(this.activePanel){
36901                 this.activePanel.getEl().clip();
36902                 
36903                 if(this.activePanel.beforeSlide){
36904                     this.activePanel.beforeSlide();
36905                 }
36906             }
36907         }
36908     },
36909     
36910     afterSlide : function(){
36911         if(Roo.isGecko){// firefox overflow auto bug workaround
36912             this.bodyEl.unclip();
36913             if(this.tabs) {
36914                 this.tabs.bodyEl.unclip();
36915             }
36916             if(this.activePanel){
36917                 this.activePanel.getEl().unclip();
36918                 if(this.activePanel.afterSlide){
36919                     this.activePanel.afterSlide();
36920                 }
36921             }
36922         }
36923     },
36924
36925     initAutoHide : function(){
36926         if(this.autoHide !== false){
36927             if(!this.autoHideHd){
36928                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36929                 this.autoHideHd = {
36930                     "mouseout": function(e){
36931                         if(!e.within(this.el, true)){
36932                             st.delay(500);
36933                         }
36934                     },
36935                     "mouseover" : function(e){
36936                         st.cancel();
36937                     },
36938                     scope : this
36939                 };
36940             }
36941             this.el.on(this.autoHideHd);
36942         }
36943     },
36944
36945     clearAutoHide : function(){
36946         if(this.autoHide !== false){
36947             this.el.un("mouseout", this.autoHideHd.mouseout);
36948             this.el.un("mouseover", this.autoHideHd.mouseover);
36949         }
36950     },
36951
36952     clearMonitor : function(){
36953         Roo.get(document).un("click", this.slideInIf, this);
36954     },
36955
36956     // these names are backwards but not changed for compat
36957     slideOut : function(){
36958         if(this.isSlid || this.el.hasActiveFx()){
36959             return;
36960         }
36961         this.isSlid = true;
36962         if(this.collapseBtn){
36963             this.collapseBtn.hide();
36964         }
36965         this.closeBtnState = this.closeBtn.getStyle('display');
36966         this.closeBtn.hide();
36967         if(this.stickBtn){
36968             this.stickBtn.show();
36969         }
36970         this.el.show();
36971         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36972         this.beforeSlide();
36973         this.el.setStyle("z-index", 10001);
36974         this.el.slideIn(this.getSlideAnchor(), {
36975             callback: function(){
36976                 this.afterSlide();
36977                 this.initAutoHide();
36978                 Roo.get(document).on("click", this.slideInIf, this);
36979                 this.fireEvent("slideshow", this);
36980             },
36981             scope: this,
36982             block: true
36983         });
36984     },
36985
36986     afterSlideIn : function(){
36987         this.clearAutoHide();
36988         this.isSlid = false;
36989         this.clearMonitor();
36990         this.el.setStyle("z-index", "");
36991         if(this.collapseBtn){
36992             this.collapseBtn.show();
36993         }
36994         this.closeBtn.setStyle('display', this.closeBtnState);
36995         if(this.stickBtn){
36996             this.stickBtn.hide();
36997         }
36998         this.fireEvent("slidehide", this);
36999     },
37000
37001     slideIn : function(cb){
37002         if(!this.isSlid || this.el.hasActiveFx()){
37003             Roo.callback(cb);
37004             return;
37005         }
37006         this.isSlid = false;
37007         this.beforeSlide();
37008         this.el.slideOut(this.getSlideAnchor(), {
37009             callback: function(){
37010                 this.el.setLeftTop(-10000, -10000);
37011                 this.afterSlide();
37012                 this.afterSlideIn();
37013                 Roo.callback(cb);
37014             },
37015             scope: this,
37016             block: true
37017         });
37018     },
37019     
37020     slideInIf : function(e){
37021         if(!e.within(this.el)){
37022             this.slideIn();
37023         }
37024     },
37025
37026     animateCollapse : function(){
37027         this.beforeSlide();
37028         this.el.setStyle("z-index", 20000);
37029         var anchor = this.getSlideAnchor();
37030         this.el.slideOut(anchor, {
37031             callback : function(){
37032                 this.el.setStyle("z-index", "");
37033                 this.collapsedEl.slideIn(anchor, {duration:.3});
37034                 this.afterSlide();
37035                 this.el.setLocation(-10000,-10000);
37036                 this.el.hide();
37037                 this.fireEvent("collapsed", this);
37038             },
37039             scope: this,
37040             block: true
37041         });
37042     },
37043
37044     animateExpand : function(){
37045         this.beforeSlide();
37046         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37047         this.el.setStyle("z-index", 20000);
37048         this.collapsedEl.hide({
37049             duration:.1
37050         });
37051         this.el.slideIn(this.getSlideAnchor(), {
37052             callback : function(){
37053                 this.el.setStyle("z-index", "");
37054                 this.afterSlide();
37055                 if(this.split){
37056                     this.split.el.show();
37057                 }
37058                 this.fireEvent("invalidated", this);
37059                 this.fireEvent("expanded", this);
37060             },
37061             scope: this,
37062             block: true
37063         });
37064     },
37065
37066     anchors : {
37067         "west" : "left",
37068         "east" : "right",
37069         "north" : "top",
37070         "south" : "bottom"
37071     },
37072
37073     sanchors : {
37074         "west" : "l",
37075         "east" : "r",
37076         "north" : "t",
37077         "south" : "b"
37078     },
37079
37080     canchors : {
37081         "west" : "tl-tr",
37082         "east" : "tr-tl",
37083         "north" : "tl-bl",
37084         "south" : "bl-tl"
37085     },
37086
37087     getAnchor : function(){
37088         return this.anchors[this.position];
37089     },
37090
37091     getCollapseAnchor : function(){
37092         return this.canchors[this.position];
37093     },
37094
37095     getSlideAnchor : function(){
37096         return this.sanchors[this.position];
37097     },
37098
37099     getAlignAdj : function(){
37100         var cm = this.cmargins;
37101         switch(this.position){
37102             case "west":
37103                 return [0, 0];
37104             break;
37105             case "east":
37106                 return [0, 0];
37107             break;
37108             case "north":
37109                 return [0, 0];
37110             break;
37111             case "south":
37112                 return [0, 0];
37113             break;
37114         }
37115     },
37116
37117     getExpandAdj : function(){
37118         var c = this.collapsedEl, cm = this.cmargins;
37119         switch(this.position){
37120             case "west":
37121                 return [-(cm.right+c.getWidth()+cm.left), 0];
37122             break;
37123             case "east":
37124                 return [cm.right+c.getWidth()+cm.left, 0];
37125             break;
37126             case "north":
37127                 return [0, -(cm.top+cm.bottom+c.getHeight())];
37128             break;
37129             case "south":
37130                 return [0, cm.top+cm.bottom+c.getHeight()];
37131             break;
37132         }
37133     }
37134 });/*
37135  * Based on:
37136  * Ext JS Library 1.1.1
37137  * Copyright(c) 2006-2007, Ext JS, LLC.
37138  *
37139  * Originally Released Under LGPL - original licence link has changed is not relivant.
37140  *
37141  * Fork - LGPL
37142  * <script type="text/javascript">
37143  */
37144 /*
37145  * These classes are private internal classes
37146  */
37147 Roo.bootstrap.layout.Center = function(config){
37148     config.region = "center";
37149     Roo.bootstrap.layout.Region.call(this, config);
37150     this.visible = true;
37151     this.minWidth = config.minWidth || 20;
37152     this.minHeight = config.minHeight || 20;
37153 };
37154
37155 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37156     hide : function(){
37157         // center panel can't be hidden
37158     },
37159     
37160     show : function(){
37161         // center panel can't be hidden
37162     },
37163     
37164     getMinWidth: function(){
37165         return this.minWidth;
37166     },
37167     
37168     getMinHeight: function(){
37169         return this.minHeight;
37170     }
37171 });
37172
37173
37174
37175
37176  
37177
37178
37179
37180
37181
37182
37183 Roo.bootstrap.layout.North = function(config)
37184 {
37185     config.region = 'north';
37186     config.cursor = 'n-resize';
37187     
37188     Roo.bootstrap.layout.Split.call(this, config);
37189     
37190     
37191     if(this.split){
37192         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37193         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37194         this.split.el.addClass("roo-layout-split-v");
37195     }
37196     var size = config.initialSize || config.height;
37197     if(typeof size != "undefined"){
37198         this.el.setHeight(size);
37199     }
37200 };
37201 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37202 {
37203     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37204     
37205     
37206     
37207     getBox : function(){
37208         if(this.collapsed){
37209             return this.collapsedEl.getBox();
37210         }
37211         var box = this.el.getBox();
37212         if(this.split){
37213             box.height += this.split.el.getHeight();
37214         }
37215         return box;
37216     },
37217     
37218     updateBox : function(box){
37219         if(this.split && !this.collapsed){
37220             box.height -= this.split.el.getHeight();
37221             this.split.el.setLeft(box.x);
37222             this.split.el.setTop(box.y+box.height);
37223             this.split.el.setWidth(box.width);
37224         }
37225         if(this.collapsed){
37226             this.updateBody(box.width, null);
37227         }
37228         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37229     }
37230 });
37231
37232
37233
37234
37235
37236 Roo.bootstrap.layout.South = function(config){
37237     config.region = 'south';
37238     config.cursor = 's-resize';
37239     Roo.bootstrap.layout.Split.call(this, config);
37240     if(this.split){
37241         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37242         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37243         this.split.el.addClass("roo-layout-split-v");
37244     }
37245     var size = config.initialSize || config.height;
37246     if(typeof size != "undefined"){
37247         this.el.setHeight(size);
37248     }
37249 };
37250
37251 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37252     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37253     getBox : function(){
37254         if(this.collapsed){
37255             return this.collapsedEl.getBox();
37256         }
37257         var box = this.el.getBox();
37258         if(this.split){
37259             var sh = this.split.el.getHeight();
37260             box.height += sh;
37261             box.y -= sh;
37262         }
37263         return box;
37264     },
37265     
37266     updateBox : function(box){
37267         if(this.split && !this.collapsed){
37268             var sh = this.split.el.getHeight();
37269             box.height -= sh;
37270             box.y += sh;
37271             this.split.el.setLeft(box.x);
37272             this.split.el.setTop(box.y-sh);
37273             this.split.el.setWidth(box.width);
37274         }
37275         if(this.collapsed){
37276             this.updateBody(box.width, null);
37277         }
37278         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37279     }
37280 });
37281
37282 Roo.bootstrap.layout.East = function(config){
37283     config.region = "east";
37284     config.cursor = "e-resize";
37285     Roo.bootstrap.layout.Split.call(this, config);
37286     if(this.split){
37287         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37288         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37289         this.split.el.addClass("roo-layout-split-h");
37290     }
37291     var size = config.initialSize || config.width;
37292     if(typeof size != "undefined"){
37293         this.el.setWidth(size);
37294     }
37295 };
37296 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37297     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37298     getBox : function(){
37299         if(this.collapsed){
37300             return this.collapsedEl.getBox();
37301         }
37302         var box = this.el.getBox();
37303         if(this.split){
37304             var sw = this.split.el.getWidth();
37305             box.width += sw;
37306             box.x -= sw;
37307         }
37308         return box;
37309     },
37310
37311     updateBox : function(box){
37312         if(this.split && !this.collapsed){
37313             var sw = this.split.el.getWidth();
37314             box.width -= sw;
37315             this.split.el.setLeft(box.x);
37316             this.split.el.setTop(box.y);
37317             this.split.el.setHeight(box.height);
37318             box.x += sw;
37319         }
37320         if(this.collapsed){
37321             this.updateBody(null, box.height);
37322         }
37323         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37324     }
37325 });
37326
37327 Roo.bootstrap.layout.West = function(config){
37328     config.region = "west";
37329     config.cursor = "w-resize";
37330     
37331     Roo.bootstrap.layout.Split.call(this, config);
37332     if(this.split){
37333         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37334         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37335         this.split.el.addClass("roo-layout-split-h");
37336     }
37337     
37338 };
37339 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37340     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37341     
37342     onRender: function(ctr, pos)
37343     {
37344         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37345         var size = this.config.initialSize || this.config.width;
37346         if(typeof size != "undefined"){
37347             this.el.setWidth(size);
37348         }
37349     },
37350     
37351     getBox : function(){
37352         if(this.collapsed){
37353             return this.collapsedEl.getBox();
37354         }
37355         var box = this.el.getBox();
37356         if(this.split){
37357             box.width += this.split.el.getWidth();
37358         }
37359         return box;
37360     },
37361     
37362     updateBox : function(box){
37363         if(this.split && !this.collapsed){
37364             var sw = this.split.el.getWidth();
37365             box.width -= sw;
37366             this.split.el.setLeft(box.x+box.width);
37367             this.split.el.setTop(box.y);
37368             this.split.el.setHeight(box.height);
37369         }
37370         if(this.collapsed){
37371             this.updateBody(null, box.height);
37372         }
37373         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37374     }
37375 });Roo.namespace("Roo.bootstrap.panel");/*
37376  * Based on:
37377  * Ext JS Library 1.1.1
37378  * Copyright(c) 2006-2007, Ext JS, LLC.
37379  *
37380  * Originally Released Under LGPL - original licence link has changed is not relivant.
37381  *
37382  * Fork - LGPL
37383  * <script type="text/javascript">
37384  */
37385 /**
37386  * @class Roo.ContentPanel
37387  * @extends Roo.util.Observable
37388  * A basic ContentPanel element.
37389  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37390  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37391  * @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
37392  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37393  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37394  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37395  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37396  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37397  * @cfg {String} title          The title for this panel
37398  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37399  * @cfg {String} url            Calls {@link #setUrl} with this value
37400  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37401  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37402  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37403  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37404  * @cfg {Boolean} badges render the badges
37405
37406  * @constructor
37407  * Create a new ContentPanel.
37408  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37409  * @param {String/Object} config A string to set only the title or a config object
37410  * @param {String} content (optional) Set the HTML content for this panel
37411  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37412  */
37413 Roo.bootstrap.panel.Content = function( config){
37414     
37415     this.tpl = config.tpl || false;
37416     
37417     var el = config.el;
37418     var content = config.content;
37419
37420     if(config.autoCreate){ // xtype is available if this is called from factory
37421         el = Roo.id();
37422     }
37423     this.el = Roo.get(el);
37424     if(!this.el && config && config.autoCreate){
37425         if(typeof config.autoCreate == "object"){
37426             if(!config.autoCreate.id){
37427                 config.autoCreate.id = config.id||el;
37428             }
37429             this.el = Roo.DomHelper.append(document.body,
37430                         config.autoCreate, true);
37431         }else{
37432             var elcfg =  {   tag: "div",
37433                             cls: "roo-layout-inactive-content",
37434                             id: config.id||el
37435                             };
37436             if (config.html) {
37437                 elcfg.html = config.html;
37438                 
37439             }
37440                         
37441             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37442         }
37443     } 
37444     this.closable = false;
37445     this.loaded = false;
37446     this.active = false;
37447    
37448       
37449     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37450         
37451         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37452         
37453         this.wrapEl = this.el; //this.el.wrap();
37454         var ti = [];
37455         if (config.toolbar.items) {
37456             ti = config.toolbar.items ;
37457             delete config.toolbar.items ;
37458         }
37459         
37460         var nitems = [];
37461         this.toolbar.render(this.wrapEl, 'before');
37462         for(var i =0;i < ti.length;i++) {
37463           //  Roo.log(['add child', items[i]]);
37464             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37465         }
37466         this.toolbar.items = nitems;
37467         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37468         delete config.toolbar;
37469         
37470     }
37471     /*
37472     // xtype created footer. - not sure if will work as we normally have to render first..
37473     if (this.footer && !this.footer.el && this.footer.xtype) {
37474         if (!this.wrapEl) {
37475             this.wrapEl = this.el.wrap();
37476         }
37477     
37478         this.footer.container = this.wrapEl.createChild();
37479          
37480         this.footer = Roo.factory(this.footer, Roo);
37481         
37482     }
37483     */
37484     
37485      if(typeof config == "string"){
37486         this.title = config;
37487     }else{
37488         Roo.apply(this, config);
37489     }
37490     
37491     if(this.resizeEl){
37492         this.resizeEl = Roo.get(this.resizeEl, true);
37493     }else{
37494         this.resizeEl = this.el;
37495     }
37496     // handle view.xtype
37497     
37498  
37499     
37500     
37501     this.addEvents({
37502         /**
37503          * @event activate
37504          * Fires when this panel is activated. 
37505          * @param {Roo.ContentPanel} this
37506          */
37507         "activate" : true,
37508         /**
37509          * @event deactivate
37510          * Fires when this panel is activated. 
37511          * @param {Roo.ContentPanel} this
37512          */
37513         "deactivate" : true,
37514
37515         /**
37516          * @event resize
37517          * Fires when this panel is resized if fitToFrame is true.
37518          * @param {Roo.ContentPanel} this
37519          * @param {Number} width The width after any component adjustments
37520          * @param {Number} height The height after any component adjustments
37521          */
37522         "resize" : true,
37523         
37524          /**
37525          * @event render
37526          * Fires when this tab is created
37527          * @param {Roo.ContentPanel} this
37528          */
37529         "render" : true
37530         
37531         
37532         
37533     });
37534     
37535
37536     
37537     
37538     if(this.autoScroll){
37539         this.resizeEl.setStyle("overflow", "auto");
37540     } else {
37541         // fix randome scrolling
37542         //this.el.on('scroll', function() {
37543         //    Roo.log('fix random scolling');
37544         //    this.scrollTo('top',0); 
37545         //});
37546     }
37547     content = content || this.content;
37548     if(content){
37549         this.setContent(content);
37550     }
37551     if(config && config.url){
37552         this.setUrl(this.url, this.params, this.loadOnce);
37553     }
37554     
37555     
37556     
37557     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37558     
37559     if (this.view && typeof(this.view.xtype) != 'undefined') {
37560         this.view.el = this.el.appendChild(document.createElement("div"));
37561         this.view = Roo.factory(this.view); 
37562         this.view.render  &&  this.view.render(false, '');  
37563     }
37564     
37565     
37566     this.fireEvent('render', this);
37567 };
37568
37569 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37570     
37571     tabTip : '',
37572     
37573     setRegion : function(region){
37574         this.region = region;
37575         this.setActiveClass(region && !this.background);
37576     },
37577     
37578     
37579     setActiveClass: function(state)
37580     {
37581         if(state){
37582            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37583            this.el.setStyle('position','relative');
37584         }else{
37585            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37586            this.el.setStyle('position', 'absolute');
37587         } 
37588     },
37589     
37590     /**
37591      * Returns the toolbar for this Panel if one was configured. 
37592      * @return {Roo.Toolbar} 
37593      */
37594     getToolbar : function(){
37595         return this.toolbar;
37596     },
37597     
37598     setActiveState : function(active)
37599     {
37600         this.active = active;
37601         this.setActiveClass(active);
37602         if(!active){
37603             if(this.fireEvent("deactivate", this) === false){
37604                 return false;
37605             }
37606             return true;
37607         }
37608         this.fireEvent("activate", this);
37609         return true;
37610     },
37611     /**
37612      * Updates this panel's element
37613      * @param {String} content The new content
37614      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37615     */
37616     setContent : function(content, loadScripts){
37617         this.el.update(content, loadScripts);
37618     },
37619
37620     ignoreResize : function(w, h){
37621         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37622             return true;
37623         }else{
37624             this.lastSize = {width: w, height: h};
37625             return false;
37626         }
37627     },
37628     /**
37629      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37630      * @return {Roo.UpdateManager} The UpdateManager
37631      */
37632     getUpdateManager : function(){
37633         return this.el.getUpdateManager();
37634     },
37635      /**
37636      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37637      * @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:
37638 <pre><code>
37639 panel.load({
37640     url: "your-url.php",
37641     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37642     callback: yourFunction,
37643     scope: yourObject, //(optional scope)
37644     discardUrl: false,
37645     nocache: false,
37646     text: "Loading...",
37647     timeout: 30,
37648     scripts: false
37649 });
37650 </code></pre>
37651      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37652      * 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.
37653      * @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}
37654      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37655      * @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.
37656      * @return {Roo.ContentPanel} this
37657      */
37658     load : function(){
37659         var um = this.el.getUpdateManager();
37660         um.update.apply(um, arguments);
37661         return this;
37662     },
37663
37664
37665     /**
37666      * 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.
37667      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37668      * @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)
37669      * @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)
37670      * @return {Roo.UpdateManager} The UpdateManager
37671      */
37672     setUrl : function(url, params, loadOnce){
37673         if(this.refreshDelegate){
37674             this.removeListener("activate", this.refreshDelegate);
37675         }
37676         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37677         this.on("activate", this.refreshDelegate);
37678         return this.el.getUpdateManager();
37679     },
37680     
37681     _handleRefresh : function(url, params, loadOnce){
37682         if(!loadOnce || !this.loaded){
37683             var updater = this.el.getUpdateManager();
37684             updater.update(url, params, this._setLoaded.createDelegate(this));
37685         }
37686     },
37687     
37688     _setLoaded : function(){
37689         this.loaded = true;
37690     }, 
37691     
37692     /**
37693      * Returns this panel's id
37694      * @return {String} 
37695      */
37696     getId : function(){
37697         return this.el.id;
37698     },
37699     
37700     /** 
37701      * Returns this panel's element - used by regiosn to add.
37702      * @return {Roo.Element} 
37703      */
37704     getEl : function(){
37705         return this.wrapEl || this.el;
37706     },
37707     
37708    
37709     
37710     adjustForComponents : function(width, height)
37711     {
37712         //Roo.log('adjustForComponents ');
37713         if(this.resizeEl != this.el){
37714             width -= this.el.getFrameWidth('lr');
37715             height -= this.el.getFrameWidth('tb');
37716         }
37717         if(this.toolbar){
37718             var te = this.toolbar.getEl();
37719             te.setWidth(width);
37720             height -= te.getHeight();
37721         }
37722         if(this.footer){
37723             var te = this.footer.getEl();
37724             te.setWidth(width);
37725             height -= te.getHeight();
37726         }
37727         
37728         
37729         if(this.adjustments){
37730             width += this.adjustments[0];
37731             height += this.adjustments[1];
37732         }
37733         return {"width": width, "height": height};
37734     },
37735     
37736     setSize : function(width, height){
37737         if(this.fitToFrame && !this.ignoreResize(width, height)){
37738             if(this.fitContainer && this.resizeEl != this.el){
37739                 this.el.setSize(width, height);
37740             }
37741             var size = this.adjustForComponents(width, height);
37742             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37743             this.fireEvent('resize', this, size.width, size.height);
37744         }
37745     },
37746     
37747     /**
37748      * Returns this panel's title
37749      * @return {String} 
37750      */
37751     getTitle : function(){
37752         
37753         if (typeof(this.title) != 'object') {
37754             return this.title;
37755         }
37756         
37757         var t = '';
37758         for (var k in this.title) {
37759             if (!this.title.hasOwnProperty(k)) {
37760                 continue;
37761             }
37762             
37763             if (k.indexOf('-') >= 0) {
37764                 var s = k.split('-');
37765                 for (var i = 0; i<s.length; i++) {
37766                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37767                 }
37768             } else {
37769                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37770             }
37771         }
37772         return t;
37773     },
37774     
37775     /**
37776      * Set this panel's title
37777      * @param {String} title
37778      */
37779     setTitle : function(title){
37780         this.title = title;
37781         if(this.region){
37782             this.region.updatePanelTitle(this, title);
37783         }
37784     },
37785     
37786     /**
37787      * Returns true is this panel was configured to be closable
37788      * @return {Boolean} 
37789      */
37790     isClosable : function(){
37791         return this.closable;
37792     },
37793     
37794     beforeSlide : function(){
37795         this.el.clip();
37796         this.resizeEl.clip();
37797     },
37798     
37799     afterSlide : function(){
37800         this.el.unclip();
37801         this.resizeEl.unclip();
37802     },
37803     
37804     /**
37805      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37806      *   Will fail silently if the {@link #setUrl} method has not been called.
37807      *   This does not activate the panel, just updates its content.
37808      */
37809     refresh : function(){
37810         if(this.refreshDelegate){
37811            this.loaded = false;
37812            this.refreshDelegate();
37813         }
37814     },
37815     
37816     /**
37817      * Destroys this panel
37818      */
37819     destroy : function(){
37820         this.el.removeAllListeners();
37821         var tempEl = document.createElement("span");
37822         tempEl.appendChild(this.el.dom);
37823         tempEl.innerHTML = "";
37824         this.el.remove();
37825         this.el = null;
37826     },
37827     
37828     /**
37829      * form - if the content panel contains a form - this is a reference to it.
37830      * @type {Roo.form.Form}
37831      */
37832     form : false,
37833     /**
37834      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37835      *    This contains a reference to it.
37836      * @type {Roo.View}
37837      */
37838     view : false,
37839     
37840       /**
37841      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37842      * <pre><code>
37843
37844 layout.addxtype({
37845        xtype : 'Form',
37846        items: [ .... ]
37847    }
37848 );
37849
37850 </code></pre>
37851      * @param {Object} cfg Xtype definition of item to add.
37852      */
37853     
37854     
37855     getChildContainer: function () {
37856         return this.getEl();
37857     }
37858     
37859     
37860     /*
37861         var  ret = new Roo.factory(cfg);
37862         return ret;
37863         
37864         
37865         // add form..
37866         if (cfg.xtype.match(/^Form$/)) {
37867             
37868             var el;
37869             //if (this.footer) {
37870             //    el = this.footer.container.insertSibling(false, 'before');
37871             //} else {
37872                 el = this.el.createChild();
37873             //}
37874
37875             this.form = new  Roo.form.Form(cfg);
37876             
37877             
37878             if ( this.form.allItems.length) {
37879                 this.form.render(el.dom);
37880             }
37881             return this.form;
37882         }
37883         // should only have one of theses..
37884         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37885             // views.. should not be just added - used named prop 'view''
37886             
37887             cfg.el = this.el.appendChild(document.createElement("div"));
37888             // factory?
37889             
37890             var ret = new Roo.factory(cfg);
37891              
37892              ret.render && ret.render(false, ''); // render blank..
37893             this.view = ret;
37894             return ret;
37895         }
37896         return false;
37897     }
37898     \*/
37899 });
37900  
37901 /**
37902  * @class Roo.bootstrap.panel.Grid
37903  * @extends Roo.bootstrap.panel.Content
37904  * @constructor
37905  * Create a new GridPanel.
37906  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37907  * @param {Object} config A the config object
37908   
37909  */
37910
37911
37912
37913 Roo.bootstrap.panel.Grid = function(config)
37914 {
37915     
37916       
37917     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37918         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37919
37920     config.el = this.wrapper;
37921     //this.el = this.wrapper;
37922     
37923       if (config.container) {
37924         // ctor'ed from a Border/panel.grid
37925         
37926         
37927         this.wrapper.setStyle("overflow", "hidden");
37928         this.wrapper.addClass('roo-grid-container');
37929
37930     }
37931     
37932     
37933     if(config.toolbar){
37934         var tool_el = this.wrapper.createChild();    
37935         this.toolbar = Roo.factory(config.toolbar);
37936         var ti = [];
37937         if (config.toolbar.items) {
37938             ti = config.toolbar.items ;
37939             delete config.toolbar.items ;
37940         }
37941         
37942         var nitems = [];
37943         this.toolbar.render(tool_el);
37944         for(var i =0;i < ti.length;i++) {
37945           //  Roo.log(['add child', items[i]]);
37946             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37947         }
37948         this.toolbar.items = nitems;
37949         
37950         delete config.toolbar;
37951     }
37952     
37953     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37954     config.grid.scrollBody = true;;
37955     config.grid.monitorWindowResize = false; // turn off autosizing
37956     config.grid.autoHeight = false;
37957     config.grid.autoWidth = false;
37958     
37959     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37960     
37961     if (config.background) {
37962         // render grid on panel activation (if panel background)
37963         this.on('activate', function(gp) {
37964             if (!gp.grid.rendered) {
37965                 gp.grid.render(this.wrapper);
37966                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37967             }
37968         });
37969             
37970     } else {
37971         this.grid.render(this.wrapper);
37972         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37973
37974     }
37975     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37976     // ??? needed ??? config.el = this.wrapper;
37977     
37978     
37979     
37980   
37981     // xtype created footer. - not sure if will work as we normally have to render first..
37982     if (this.footer && !this.footer.el && this.footer.xtype) {
37983         
37984         var ctr = this.grid.getView().getFooterPanel(true);
37985         this.footer.dataSource = this.grid.dataSource;
37986         this.footer = Roo.factory(this.footer, Roo);
37987         this.footer.render(ctr);
37988         
37989     }
37990     
37991     
37992     
37993     
37994      
37995 };
37996
37997 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37998     getId : function(){
37999         return this.grid.id;
38000     },
38001     
38002     /**
38003      * Returns the grid for this panel
38004      * @return {Roo.bootstrap.Table} 
38005      */
38006     getGrid : function(){
38007         return this.grid;    
38008     },
38009     
38010     setSize : function(width, height){
38011         if(!this.ignoreResize(width, height)){
38012             var grid = this.grid;
38013             var size = this.adjustForComponents(width, height);
38014             var gridel = grid.getGridEl();
38015             gridel.setSize(size.width, size.height);
38016             /*
38017             var thd = grid.getGridEl().select('thead',true).first();
38018             var tbd = grid.getGridEl().select('tbody', true).first();
38019             if (tbd) {
38020                 tbd.setSize(width, height - thd.getHeight());
38021             }
38022             */
38023             grid.autoSize();
38024         }
38025     },
38026      
38027     
38028     
38029     beforeSlide : function(){
38030         this.grid.getView().scroller.clip();
38031     },
38032     
38033     afterSlide : function(){
38034         this.grid.getView().scroller.unclip();
38035     },
38036     
38037     destroy : function(){
38038         this.grid.destroy();
38039         delete this.grid;
38040         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
38041     }
38042 });
38043
38044 /**
38045  * @class Roo.bootstrap.panel.Nest
38046  * @extends Roo.bootstrap.panel.Content
38047  * @constructor
38048  * Create a new Panel, that can contain a layout.Border.
38049  * 
38050  * 
38051  * @param {Roo.BorderLayout} layout The layout for this panel
38052  * @param {String/Object} config A string to set only the title or a config object
38053  */
38054 Roo.bootstrap.panel.Nest = function(config)
38055 {
38056     // construct with only one argument..
38057     /* FIXME - implement nicer consturctors
38058     if (layout.layout) {
38059         config = layout;
38060         layout = config.layout;
38061         delete config.layout;
38062     }
38063     if (layout.xtype && !layout.getEl) {
38064         // then layout needs constructing..
38065         layout = Roo.factory(layout, Roo);
38066     }
38067     */
38068     
38069     config.el =  config.layout.getEl();
38070     
38071     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38072     
38073     config.layout.monitorWindowResize = false; // turn off autosizing
38074     this.layout = config.layout;
38075     this.layout.getEl().addClass("roo-layout-nested-layout");
38076     this.layout.parent = this;
38077     
38078     
38079     
38080     
38081 };
38082
38083 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38084
38085     setSize : function(width, height){
38086         if(!this.ignoreResize(width, height)){
38087             var size = this.adjustForComponents(width, height);
38088             var el = this.layout.getEl();
38089             if (size.height < 1) {
38090                 el.setWidth(size.width);   
38091             } else {
38092                 el.setSize(size.width, size.height);
38093             }
38094             var touch = el.dom.offsetWidth;
38095             this.layout.layout();
38096             // ie requires a double layout on the first pass
38097             if(Roo.isIE && !this.initialized){
38098                 this.initialized = true;
38099                 this.layout.layout();
38100             }
38101         }
38102     },
38103     
38104     // activate all subpanels if not currently active..
38105     
38106     setActiveState : function(active){
38107         this.active = active;
38108         this.setActiveClass(active);
38109         
38110         if(!active){
38111             this.fireEvent("deactivate", this);
38112             return;
38113         }
38114         
38115         this.fireEvent("activate", this);
38116         // not sure if this should happen before or after..
38117         if (!this.layout) {
38118             return; // should not happen..
38119         }
38120         var reg = false;
38121         for (var r in this.layout.regions) {
38122             reg = this.layout.getRegion(r);
38123             if (reg.getActivePanel()) {
38124                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
38125                 reg.setActivePanel(reg.getActivePanel());
38126                 continue;
38127             }
38128             if (!reg.panels.length) {
38129                 continue;
38130             }
38131             reg.showPanel(reg.getPanel(0));
38132         }
38133         
38134         
38135         
38136         
38137     },
38138     
38139     /**
38140      * Returns the nested BorderLayout for this panel
38141      * @return {Roo.BorderLayout} 
38142      */
38143     getLayout : function(){
38144         return this.layout;
38145     },
38146     
38147      /**
38148      * Adds a xtype elements to the layout of the nested panel
38149      * <pre><code>
38150
38151 panel.addxtype({
38152        xtype : 'ContentPanel',
38153        region: 'west',
38154        items: [ .... ]
38155    }
38156 );
38157
38158 panel.addxtype({
38159         xtype : 'NestedLayoutPanel',
38160         region: 'west',
38161         layout: {
38162            center: { },
38163            west: { }   
38164         },
38165         items : [ ... list of content panels or nested layout panels.. ]
38166    }
38167 );
38168 </code></pre>
38169      * @param {Object} cfg Xtype definition of item to add.
38170      */
38171     addxtype : function(cfg) {
38172         return this.layout.addxtype(cfg);
38173     
38174     }
38175 });/*
38176  * Based on:
38177  * Ext JS Library 1.1.1
38178  * Copyright(c) 2006-2007, Ext JS, LLC.
38179  *
38180  * Originally Released Under LGPL - original licence link has changed is not relivant.
38181  *
38182  * Fork - LGPL
38183  * <script type="text/javascript">
38184  */
38185 /**
38186  * @class Roo.TabPanel
38187  * @extends Roo.util.Observable
38188  * A lightweight tab container.
38189  * <br><br>
38190  * Usage:
38191  * <pre><code>
38192 // basic tabs 1, built from existing content
38193 var tabs = new Roo.TabPanel("tabs1");
38194 tabs.addTab("script", "View Script");
38195 tabs.addTab("markup", "View Markup");
38196 tabs.activate("script");
38197
38198 // more advanced tabs, built from javascript
38199 var jtabs = new Roo.TabPanel("jtabs");
38200 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38201
38202 // set up the UpdateManager
38203 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38204 var updater = tab2.getUpdateManager();
38205 updater.setDefaultUrl("ajax1.htm");
38206 tab2.on('activate', updater.refresh, updater, true);
38207
38208 // Use setUrl for Ajax loading
38209 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38210 tab3.setUrl("ajax2.htm", null, true);
38211
38212 // Disabled tab
38213 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38214 tab4.disable();
38215
38216 jtabs.activate("jtabs-1");
38217  * </code></pre>
38218  * @constructor
38219  * Create a new TabPanel.
38220  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38221  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38222  */
38223 Roo.bootstrap.panel.Tabs = function(config){
38224     /**
38225     * The container element for this TabPanel.
38226     * @type Roo.Element
38227     */
38228     this.el = Roo.get(config.el);
38229     delete config.el;
38230     if(config){
38231         if(typeof config == "boolean"){
38232             this.tabPosition = config ? "bottom" : "top";
38233         }else{
38234             Roo.apply(this, config);
38235         }
38236     }
38237     
38238     if(this.tabPosition == "bottom"){
38239         // if tabs are at the bottom = create the body first.
38240         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38241         this.el.addClass("roo-tabs-bottom");
38242     }
38243     // next create the tabs holders
38244     
38245     if (this.tabPosition == "west"){
38246         
38247         var reg = this.region; // fake it..
38248         while (reg) {
38249             if (!reg.mgr.parent) {
38250                 break;
38251             }
38252             reg = reg.mgr.parent.region;
38253         }
38254         Roo.log("got nest?");
38255         Roo.log(reg);
38256         if (reg.mgr.getRegion('west')) {
38257             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38258             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38259             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38260             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38261             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38262         
38263             
38264         }
38265         
38266         
38267     } else {
38268      
38269         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38270         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38271         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38272         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38273     }
38274     
38275     
38276     if(Roo.isIE){
38277         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38278     }
38279     
38280     // finally - if tabs are at the top, then create the body last..
38281     if(this.tabPosition != "bottom"){
38282         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38283          * @type Roo.Element
38284          */
38285         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38286         this.el.addClass("roo-tabs-top");
38287     }
38288     this.items = [];
38289
38290     this.bodyEl.setStyle("position", "relative");
38291
38292     this.active = null;
38293     this.activateDelegate = this.activate.createDelegate(this);
38294
38295     this.addEvents({
38296         /**
38297          * @event tabchange
38298          * Fires when the active tab changes
38299          * @param {Roo.TabPanel} this
38300          * @param {Roo.TabPanelItem} activePanel The new active tab
38301          */
38302         "tabchange": true,
38303         /**
38304          * @event beforetabchange
38305          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38306          * @param {Roo.TabPanel} this
38307          * @param {Object} e Set cancel to true on this object to cancel the tab change
38308          * @param {Roo.TabPanelItem} tab The tab being changed to
38309          */
38310         "beforetabchange" : true
38311     });
38312
38313     Roo.EventManager.onWindowResize(this.onResize, this);
38314     this.cpad = this.el.getPadding("lr");
38315     this.hiddenCount = 0;
38316
38317
38318     // toolbar on the tabbar support...
38319     if (this.toolbar) {
38320         alert("no toolbar support yet");
38321         this.toolbar  = false;
38322         /*
38323         var tcfg = this.toolbar;
38324         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38325         this.toolbar = new Roo.Toolbar(tcfg);
38326         if (Roo.isSafari) {
38327             var tbl = tcfg.container.child('table', true);
38328             tbl.setAttribute('width', '100%');
38329         }
38330         */
38331         
38332     }
38333    
38334
38335
38336     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38337 };
38338
38339 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38340     /*
38341      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38342      */
38343     tabPosition : "top",
38344     /*
38345      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38346      */
38347     currentTabWidth : 0,
38348     /*
38349      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38350      */
38351     minTabWidth : 40,
38352     /*
38353      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38354      */
38355     maxTabWidth : 250,
38356     /*
38357      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38358      */
38359     preferredTabWidth : 175,
38360     /*
38361      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38362      */
38363     resizeTabs : false,
38364     /*
38365      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38366      */
38367     monitorResize : true,
38368     /*
38369      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38370      */
38371     toolbar : false,  // set by caller..
38372     
38373     region : false, /// set by caller
38374     
38375     disableTooltips : true, // not used yet...
38376
38377     /**
38378      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38379      * @param {String} id The id of the div to use <b>or create</b>
38380      * @param {String} text The text for the tab
38381      * @param {String} content (optional) Content to put in the TabPanelItem body
38382      * @param {Boolean} closable (optional) True to create a close icon on the tab
38383      * @return {Roo.TabPanelItem} The created TabPanelItem
38384      */
38385     addTab : function(id, text, content, closable, tpl)
38386     {
38387         var item = new Roo.bootstrap.panel.TabItem({
38388             panel: this,
38389             id : id,
38390             text : text,
38391             closable : closable,
38392             tpl : tpl
38393         });
38394         this.addTabItem(item);
38395         if(content){
38396             item.setContent(content);
38397         }
38398         return item;
38399     },
38400
38401     /**
38402      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38403      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38404      * @return {Roo.TabPanelItem}
38405      */
38406     getTab : function(id){
38407         return this.items[id];
38408     },
38409
38410     /**
38411      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38412      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38413      */
38414     hideTab : function(id){
38415         var t = this.items[id];
38416         if(!t.isHidden()){
38417            t.setHidden(true);
38418            this.hiddenCount++;
38419            this.autoSizeTabs();
38420         }
38421     },
38422
38423     /**
38424      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38425      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38426      */
38427     unhideTab : function(id){
38428         var t = this.items[id];
38429         if(t.isHidden()){
38430            t.setHidden(false);
38431            this.hiddenCount--;
38432            this.autoSizeTabs();
38433         }
38434     },
38435
38436     /**
38437      * Adds an existing {@link Roo.TabPanelItem}.
38438      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38439      */
38440     addTabItem : function(item)
38441     {
38442         this.items[item.id] = item;
38443         this.items.push(item);
38444         this.autoSizeTabs();
38445       //  if(this.resizeTabs){
38446     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38447   //         this.autoSizeTabs();
38448 //        }else{
38449 //            item.autoSize();
38450        // }
38451     },
38452
38453     /**
38454      * Removes a {@link Roo.TabPanelItem}.
38455      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38456      */
38457     removeTab : function(id){
38458         var items = this.items;
38459         var tab = items[id];
38460         if(!tab) { return; }
38461         var index = items.indexOf(tab);
38462         if(this.active == tab && items.length > 1){
38463             var newTab = this.getNextAvailable(index);
38464             if(newTab) {
38465                 newTab.activate();
38466             }
38467         }
38468         this.stripEl.dom.removeChild(tab.pnode.dom);
38469         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38470             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38471         }
38472         items.splice(index, 1);
38473         delete this.items[tab.id];
38474         tab.fireEvent("close", tab);
38475         tab.purgeListeners();
38476         this.autoSizeTabs();
38477     },
38478
38479     getNextAvailable : function(start){
38480         var items = this.items;
38481         var index = start;
38482         // look for a next tab that will slide over to
38483         // replace the one being removed
38484         while(index < items.length){
38485             var item = items[++index];
38486             if(item && !item.isHidden()){
38487                 return item;
38488             }
38489         }
38490         // if one isn't found select the previous tab (on the left)
38491         index = start;
38492         while(index >= 0){
38493             var item = items[--index];
38494             if(item && !item.isHidden()){
38495                 return item;
38496             }
38497         }
38498         return null;
38499     },
38500
38501     /**
38502      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38503      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38504      */
38505     disableTab : function(id){
38506         var tab = this.items[id];
38507         if(tab && this.active != tab){
38508             tab.disable();
38509         }
38510     },
38511
38512     /**
38513      * Enables a {@link Roo.TabPanelItem} that is disabled.
38514      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38515      */
38516     enableTab : function(id){
38517         var tab = this.items[id];
38518         tab.enable();
38519     },
38520
38521     /**
38522      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38523      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38524      * @return {Roo.TabPanelItem} The TabPanelItem.
38525      */
38526     activate : function(id)
38527     {
38528         //Roo.log('activite:'  + id);
38529         
38530         var tab = this.items[id];
38531         if(!tab){
38532             return null;
38533         }
38534         if(tab == this.active || tab.disabled){
38535             return tab;
38536         }
38537         var e = {};
38538         this.fireEvent("beforetabchange", this, e, tab);
38539         if(e.cancel !== true && !tab.disabled){
38540             if(this.active){
38541                 this.active.hide();
38542             }
38543             this.active = this.items[id];
38544             this.active.show();
38545             this.fireEvent("tabchange", this, this.active);
38546         }
38547         return tab;
38548     },
38549
38550     /**
38551      * Gets the active {@link Roo.TabPanelItem}.
38552      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38553      */
38554     getActiveTab : function(){
38555         return this.active;
38556     },
38557
38558     /**
38559      * Updates the tab body element to fit the height of the container element
38560      * for overflow scrolling
38561      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38562      */
38563     syncHeight : function(targetHeight){
38564         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38565         var bm = this.bodyEl.getMargins();
38566         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38567         this.bodyEl.setHeight(newHeight);
38568         return newHeight;
38569     },
38570
38571     onResize : function(){
38572         if(this.monitorResize){
38573             this.autoSizeTabs();
38574         }
38575     },
38576
38577     /**
38578      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38579      */
38580     beginUpdate : function(){
38581         this.updating = true;
38582     },
38583
38584     /**
38585      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38586      */
38587     endUpdate : function(){
38588         this.updating = false;
38589         this.autoSizeTabs();
38590     },
38591
38592     /**
38593      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38594      */
38595     autoSizeTabs : function()
38596     {
38597         var count = this.items.length;
38598         var vcount = count - this.hiddenCount;
38599         
38600         if (vcount < 2) {
38601             this.stripEl.hide();
38602         } else {
38603             this.stripEl.show();
38604         }
38605         
38606         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38607             return;
38608         }
38609         
38610         
38611         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38612         var availWidth = Math.floor(w / vcount);
38613         var b = this.stripBody;
38614         if(b.getWidth() > w){
38615             var tabs = this.items;
38616             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38617             if(availWidth < this.minTabWidth){
38618                 /*if(!this.sleft){    // incomplete scrolling code
38619                     this.createScrollButtons();
38620                 }
38621                 this.showScroll();
38622                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38623             }
38624         }else{
38625             if(this.currentTabWidth < this.preferredTabWidth){
38626                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38627             }
38628         }
38629     },
38630
38631     /**
38632      * Returns the number of tabs in this TabPanel.
38633      * @return {Number}
38634      */
38635      getCount : function(){
38636          return this.items.length;
38637      },
38638
38639     /**
38640      * Resizes all the tabs to the passed width
38641      * @param {Number} The new width
38642      */
38643     setTabWidth : function(width){
38644         this.currentTabWidth = width;
38645         for(var i = 0, len = this.items.length; i < len; i++) {
38646                 if(!this.items[i].isHidden()) {
38647                 this.items[i].setWidth(width);
38648             }
38649         }
38650     },
38651
38652     /**
38653      * Destroys this TabPanel
38654      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38655      */
38656     destroy : function(removeEl){
38657         Roo.EventManager.removeResizeListener(this.onResize, this);
38658         for(var i = 0, len = this.items.length; i < len; i++){
38659             this.items[i].purgeListeners();
38660         }
38661         if(removeEl === true){
38662             this.el.update("");
38663             this.el.remove();
38664         }
38665     },
38666     
38667     createStrip : function(container)
38668     {
38669         var strip = document.createElement("nav");
38670         strip.className = Roo.bootstrap.version == 4 ?
38671             "navbar-light bg-light" : 
38672             "navbar navbar-default"; //"x-tabs-wrap";
38673         container.appendChild(strip);
38674         return strip;
38675     },
38676     
38677     createStripList : function(strip)
38678     {
38679         // div wrapper for retard IE
38680         // returns the "tr" element.
38681         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38682         //'<div class="x-tabs-strip-wrap">'+
38683           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38684           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38685         return strip.firstChild; //.firstChild.firstChild.firstChild;
38686     },
38687     createBody : function(container)
38688     {
38689         var body = document.createElement("div");
38690         Roo.id(body, "tab-body");
38691         //Roo.fly(body).addClass("x-tabs-body");
38692         Roo.fly(body).addClass("tab-content");
38693         container.appendChild(body);
38694         return body;
38695     },
38696     createItemBody :function(bodyEl, id){
38697         var body = Roo.getDom(id);
38698         if(!body){
38699             body = document.createElement("div");
38700             body.id = id;
38701         }
38702         //Roo.fly(body).addClass("x-tabs-item-body");
38703         Roo.fly(body).addClass("tab-pane");
38704          bodyEl.insertBefore(body, bodyEl.firstChild);
38705         return body;
38706     },
38707     /** @private */
38708     createStripElements :  function(stripEl, text, closable, tpl)
38709     {
38710         var td = document.createElement("li"); // was td..
38711         td.className = 'nav-item';
38712         
38713         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38714         
38715         
38716         stripEl.appendChild(td);
38717         /*if(closable){
38718             td.className = "x-tabs-closable";
38719             if(!this.closeTpl){
38720                 this.closeTpl = new Roo.Template(
38721                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38722                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38723                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38724                 );
38725             }
38726             var el = this.closeTpl.overwrite(td, {"text": text});
38727             var close = el.getElementsByTagName("div")[0];
38728             var inner = el.getElementsByTagName("em")[0];
38729             return {"el": el, "close": close, "inner": inner};
38730         } else {
38731         */
38732         // not sure what this is..
38733 //            if(!this.tabTpl){
38734                 //this.tabTpl = new Roo.Template(
38735                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38736                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38737                 //);
38738 //                this.tabTpl = new Roo.Template(
38739 //                   '<a href="#">' +
38740 //                   '<span unselectable="on"' +
38741 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38742 //                            ' >{text}</span></a>'
38743 //                );
38744 //                
38745 //            }
38746
38747
38748             var template = tpl || this.tabTpl || false;
38749             
38750             if(!template){
38751                 template =  new Roo.Template(
38752                         Roo.bootstrap.version == 4 ? 
38753                             (
38754                                 '<a class="nav-link" href="#" unselectable="on"' +
38755                                      (this.disableTooltips ? '' : ' title="{text}"') +
38756                                      ' >{text}</a>'
38757                             ) : (
38758                                 '<a class="nav-link" href="#">' +
38759                                 '<span unselectable="on"' +
38760                                          (this.disableTooltips ? '' : ' title="{text}"') +
38761                                     ' >{text}</span></a>'
38762                             )
38763                 );
38764             }
38765             
38766             switch (typeof(template)) {
38767                 case 'object' :
38768                     break;
38769                 case 'string' :
38770                     template = new Roo.Template(template);
38771                     break;
38772                 default :
38773                     break;
38774             }
38775             
38776             var el = template.overwrite(td, {"text": text});
38777             
38778             var inner = el.getElementsByTagName("span")[0];
38779             
38780             return {"el": el, "inner": inner};
38781             
38782     }
38783         
38784     
38785 });
38786
38787 /**
38788  * @class Roo.TabPanelItem
38789  * @extends Roo.util.Observable
38790  * Represents an individual item (tab plus body) in a TabPanel.
38791  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38792  * @param {String} id The id of this TabPanelItem
38793  * @param {String} text The text for the tab of this TabPanelItem
38794  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38795  */
38796 Roo.bootstrap.panel.TabItem = function(config){
38797     /**
38798      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38799      * @type Roo.TabPanel
38800      */
38801     this.tabPanel = config.panel;
38802     /**
38803      * The id for this TabPanelItem
38804      * @type String
38805      */
38806     this.id = config.id;
38807     /** @private */
38808     this.disabled = false;
38809     /** @private */
38810     this.text = config.text;
38811     /** @private */
38812     this.loaded = false;
38813     this.closable = config.closable;
38814
38815     /**
38816      * The body element for this TabPanelItem.
38817      * @type Roo.Element
38818      */
38819     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38820     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38821     this.bodyEl.setStyle("display", "block");
38822     this.bodyEl.setStyle("zoom", "1");
38823     //this.hideAction();
38824
38825     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38826     /** @private */
38827     this.el = Roo.get(els.el);
38828     this.inner = Roo.get(els.inner, true);
38829      this.textEl = Roo.bootstrap.version == 4 ?
38830         this.el : Roo.get(this.el.dom.firstChild, true);
38831
38832     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38833     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38834
38835     
38836 //    this.el.on("mousedown", this.onTabMouseDown, this);
38837     this.el.on("click", this.onTabClick, this);
38838     /** @private */
38839     if(config.closable){
38840         var c = Roo.get(els.close, true);
38841         c.dom.title = this.closeText;
38842         c.addClassOnOver("close-over");
38843         c.on("click", this.closeClick, this);
38844      }
38845
38846     this.addEvents({
38847          /**
38848          * @event activate
38849          * Fires when this tab becomes the active tab.
38850          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38851          * @param {Roo.TabPanelItem} this
38852          */
38853         "activate": true,
38854         /**
38855          * @event beforeclose
38856          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38857          * @param {Roo.TabPanelItem} this
38858          * @param {Object} e Set cancel to true on this object to cancel the close.
38859          */
38860         "beforeclose": true,
38861         /**
38862          * @event close
38863          * Fires when this tab is closed.
38864          * @param {Roo.TabPanelItem} this
38865          */
38866          "close": true,
38867         /**
38868          * @event deactivate
38869          * Fires when this tab is no longer the active tab.
38870          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38871          * @param {Roo.TabPanelItem} this
38872          */
38873          "deactivate" : true
38874     });
38875     this.hidden = false;
38876
38877     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38878 };
38879
38880 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38881            {
38882     purgeListeners : function(){
38883        Roo.util.Observable.prototype.purgeListeners.call(this);
38884        this.el.removeAllListeners();
38885     },
38886     /**
38887      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38888      */
38889     show : function(){
38890         this.status_node.addClass("active");
38891         this.showAction();
38892         if(Roo.isOpera){
38893             this.tabPanel.stripWrap.repaint();
38894         }
38895         this.fireEvent("activate", this.tabPanel, this);
38896     },
38897
38898     /**
38899      * Returns true if this tab is the active tab.
38900      * @return {Boolean}
38901      */
38902     isActive : function(){
38903         return this.tabPanel.getActiveTab() == this;
38904     },
38905
38906     /**
38907      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38908      */
38909     hide : function(){
38910         this.status_node.removeClass("active");
38911         this.hideAction();
38912         this.fireEvent("deactivate", this.tabPanel, this);
38913     },
38914
38915     hideAction : function(){
38916         this.bodyEl.hide();
38917         this.bodyEl.setStyle("position", "absolute");
38918         this.bodyEl.setLeft("-20000px");
38919         this.bodyEl.setTop("-20000px");
38920     },
38921
38922     showAction : function(){
38923         this.bodyEl.setStyle("position", "relative");
38924         this.bodyEl.setTop("");
38925         this.bodyEl.setLeft("");
38926         this.bodyEl.show();
38927     },
38928
38929     /**
38930      * Set the tooltip for the tab.
38931      * @param {String} tooltip The tab's tooltip
38932      */
38933     setTooltip : function(text){
38934         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38935             this.textEl.dom.qtip = text;
38936             this.textEl.dom.removeAttribute('title');
38937         }else{
38938             this.textEl.dom.title = text;
38939         }
38940     },
38941
38942     onTabClick : function(e){
38943         e.preventDefault();
38944         this.tabPanel.activate(this.id);
38945     },
38946
38947     onTabMouseDown : function(e){
38948         e.preventDefault();
38949         this.tabPanel.activate(this.id);
38950     },
38951 /*
38952     getWidth : function(){
38953         return this.inner.getWidth();
38954     },
38955
38956     setWidth : function(width){
38957         var iwidth = width - this.linode.getPadding("lr");
38958         this.inner.setWidth(iwidth);
38959         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38960         this.linode.setWidth(width);
38961     },
38962 */
38963     /**
38964      * Show or hide the tab
38965      * @param {Boolean} hidden True to hide or false to show.
38966      */
38967     setHidden : function(hidden){
38968         this.hidden = hidden;
38969         this.linode.setStyle("display", hidden ? "none" : "");
38970     },
38971
38972     /**
38973      * Returns true if this tab is "hidden"
38974      * @return {Boolean}
38975      */
38976     isHidden : function(){
38977         return this.hidden;
38978     },
38979
38980     /**
38981      * Returns the text for this tab
38982      * @return {String}
38983      */
38984     getText : function(){
38985         return this.text;
38986     },
38987     /*
38988     autoSize : function(){
38989         //this.el.beginMeasure();
38990         this.textEl.setWidth(1);
38991         /*
38992          *  #2804 [new] Tabs in Roojs
38993          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38994          */
38995         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38996         //this.el.endMeasure();
38997     //},
38998
38999     /**
39000      * Sets the text for the tab (Note: this also sets the tooltip text)
39001      * @param {String} text The tab's text and tooltip
39002      */
39003     setText : function(text){
39004         this.text = text;
39005         this.textEl.update(text);
39006         this.setTooltip(text);
39007         //if(!this.tabPanel.resizeTabs){
39008         //    this.autoSize();
39009         //}
39010     },
39011     /**
39012      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39013      */
39014     activate : function(){
39015         this.tabPanel.activate(this.id);
39016     },
39017
39018     /**
39019      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39020      */
39021     disable : function(){
39022         if(this.tabPanel.active != this){
39023             this.disabled = true;
39024             this.status_node.addClass("disabled");
39025         }
39026     },
39027
39028     /**
39029      * Enables this TabPanelItem if it was previously disabled.
39030      */
39031     enable : function(){
39032         this.disabled = false;
39033         this.status_node.removeClass("disabled");
39034     },
39035
39036     /**
39037      * Sets the content for this TabPanelItem.
39038      * @param {String} content The content
39039      * @param {Boolean} loadScripts true to look for and load scripts
39040      */
39041     setContent : function(content, loadScripts){
39042         this.bodyEl.update(content, loadScripts);
39043     },
39044
39045     /**
39046      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39047      * @return {Roo.UpdateManager} The UpdateManager
39048      */
39049     getUpdateManager : function(){
39050         return this.bodyEl.getUpdateManager();
39051     },
39052
39053     /**
39054      * Set a URL to be used to load the content for this TabPanelItem.
39055      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39056      * @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)
39057      * @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)
39058      * @return {Roo.UpdateManager} The UpdateManager
39059      */
39060     setUrl : function(url, params, loadOnce){
39061         if(this.refreshDelegate){
39062             this.un('activate', this.refreshDelegate);
39063         }
39064         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39065         this.on("activate", this.refreshDelegate);
39066         return this.bodyEl.getUpdateManager();
39067     },
39068
39069     /** @private */
39070     _handleRefresh : function(url, params, loadOnce){
39071         if(!loadOnce || !this.loaded){
39072             var updater = this.bodyEl.getUpdateManager();
39073             updater.update(url, params, this._setLoaded.createDelegate(this));
39074         }
39075     },
39076
39077     /**
39078      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
39079      *   Will fail silently if the setUrl method has not been called.
39080      *   This does not activate the panel, just updates its content.
39081      */
39082     refresh : function(){
39083         if(this.refreshDelegate){
39084            this.loaded = false;
39085            this.refreshDelegate();
39086         }
39087     },
39088
39089     /** @private */
39090     _setLoaded : function(){
39091         this.loaded = true;
39092     },
39093
39094     /** @private */
39095     closeClick : function(e){
39096         var o = {};
39097         e.stopEvent();
39098         this.fireEvent("beforeclose", this, o);
39099         if(o.cancel !== true){
39100             this.tabPanel.removeTab(this.id);
39101         }
39102     },
39103     /**
39104      * The text displayed in the tooltip for the close icon.
39105      * @type String
39106      */
39107     closeText : "Close this tab"
39108 });
39109 /**
39110 *    This script refer to:
39111 *    Title: International Telephone Input
39112 *    Author: Jack O'Connor
39113 *    Code version:  v12.1.12
39114 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39115 **/
39116
39117 Roo.bootstrap.PhoneInputData = function() {
39118     var d = [
39119       [
39120         "Afghanistan (‫افغانستان‬‎)",
39121         "af",
39122         "93"
39123       ],
39124       [
39125         "Albania (Shqipëri)",
39126         "al",
39127         "355"
39128       ],
39129       [
39130         "Algeria (‫الجزائر‬‎)",
39131         "dz",
39132         "213"
39133       ],
39134       [
39135         "American Samoa",
39136         "as",
39137         "1684"
39138       ],
39139       [
39140         "Andorra",
39141         "ad",
39142         "376"
39143       ],
39144       [
39145         "Angola",
39146         "ao",
39147         "244"
39148       ],
39149       [
39150         "Anguilla",
39151         "ai",
39152         "1264"
39153       ],
39154       [
39155         "Antigua and Barbuda",
39156         "ag",
39157         "1268"
39158       ],
39159       [
39160         "Argentina",
39161         "ar",
39162         "54"
39163       ],
39164       [
39165         "Armenia (Հայաստան)",
39166         "am",
39167         "374"
39168       ],
39169       [
39170         "Aruba",
39171         "aw",
39172         "297"
39173       ],
39174       [
39175         "Australia",
39176         "au",
39177         "61",
39178         0
39179       ],
39180       [
39181         "Austria (Österreich)",
39182         "at",
39183         "43"
39184       ],
39185       [
39186         "Azerbaijan (Azərbaycan)",
39187         "az",
39188         "994"
39189       ],
39190       [
39191         "Bahamas",
39192         "bs",
39193         "1242"
39194       ],
39195       [
39196         "Bahrain (‫البحرين‬‎)",
39197         "bh",
39198         "973"
39199       ],
39200       [
39201         "Bangladesh (বাংলাদেশ)",
39202         "bd",
39203         "880"
39204       ],
39205       [
39206         "Barbados",
39207         "bb",
39208         "1246"
39209       ],
39210       [
39211         "Belarus (Беларусь)",
39212         "by",
39213         "375"
39214       ],
39215       [
39216         "Belgium (België)",
39217         "be",
39218         "32"
39219       ],
39220       [
39221         "Belize",
39222         "bz",
39223         "501"
39224       ],
39225       [
39226         "Benin (Bénin)",
39227         "bj",
39228         "229"
39229       ],
39230       [
39231         "Bermuda",
39232         "bm",
39233         "1441"
39234       ],
39235       [
39236         "Bhutan (འབྲུག)",
39237         "bt",
39238         "975"
39239       ],
39240       [
39241         "Bolivia",
39242         "bo",
39243         "591"
39244       ],
39245       [
39246         "Bosnia and Herzegovina (Босна и Херцеговина)",
39247         "ba",
39248         "387"
39249       ],
39250       [
39251         "Botswana",
39252         "bw",
39253         "267"
39254       ],
39255       [
39256         "Brazil (Brasil)",
39257         "br",
39258         "55"
39259       ],
39260       [
39261         "British Indian Ocean Territory",
39262         "io",
39263         "246"
39264       ],
39265       [
39266         "British Virgin Islands",
39267         "vg",
39268         "1284"
39269       ],
39270       [
39271         "Brunei",
39272         "bn",
39273         "673"
39274       ],
39275       [
39276         "Bulgaria (България)",
39277         "bg",
39278         "359"
39279       ],
39280       [
39281         "Burkina Faso",
39282         "bf",
39283         "226"
39284       ],
39285       [
39286         "Burundi (Uburundi)",
39287         "bi",
39288         "257"
39289       ],
39290       [
39291         "Cambodia (កម្ពុជា)",
39292         "kh",
39293         "855"
39294       ],
39295       [
39296         "Cameroon (Cameroun)",
39297         "cm",
39298         "237"
39299       ],
39300       [
39301         "Canada",
39302         "ca",
39303         "1",
39304         1,
39305         ["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"]
39306       ],
39307       [
39308         "Cape Verde (Kabu Verdi)",
39309         "cv",
39310         "238"
39311       ],
39312       [
39313         "Caribbean Netherlands",
39314         "bq",
39315         "599",
39316         1
39317       ],
39318       [
39319         "Cayman Islands",
39320         "ky",
39321         "1345"
39322       ],
39323       [
39324         "Central African Republic (République centrafricaine)",
39325         "cf",
39326         "236"
39327       ],
39328       [
39329         "Chad (Tchad)",
39330         "td",
39331         "235"
39332       ],
39333       [
39334         "Chile",
39335         "cl",
39336         "56"
39337       ],
39338       [
39339         "China (中国)",
39340         "cn",
39341         "86"
39342       ],
39343       [
39344         "Christmas Island",
39345         "cx",
39346         "61",
39347         2
39348       ],
39349       [
39350         "Cocos (Keeling) Islands",
39351         "cc",
39352         "61",
39353         1
39354       ],
39355       [
39356         "Colombia",
39357         "co",
39358         "57"
39359       ],
39360       [
39361         "Comoros (‫جزر القمر‬‎)",
39362         "km",
39363         "269"
39364       ],
39365       [
39366         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39367         "cd",
39368         "243"
39369       ],
39370       [
39371         "Congo (Republic) (Congo-Brazzaville)",
39372         "cg",
39373         "242"
39374       ],
39375       [
39376         "Cook Islands",
39377         "ck",
39378         "682"
39379       ],
39380       [
39381         "Costa Rica",
39382         "cr",
39383         "506"
39384       ],
39385       [
39386         "Côte d’Ivoire",
39387         "ci",
39388         "225"
39389       ],
39390       [
39391         "Croatia (Hrvatska)",
39392         "hr",
39393         "385"
39394       ],
39395       [
39396         "Cuba",
39397         "cu",
39398         "53"
39399       ],
39400       [
39401         "Curaçao",
39402         "cw",
39403         "599",
39404         0
39405       ],
39406       [
39407         "Cyprus (Κύπρος)",
39408         "cy",
39409         "357"
39410       ],
39411       [
39412         "Czech Republic (Česká republika)",
39413         "cz",
39414         "420"
39415       ],
39416       [
39417         "Denmark (Danmark)",
39418         "dk",
39419         "45"
39420       ],
39421       [
39422         "Djibouti",
39423         "dj",
39424         "253"
39425       ],
39426       [
39427         "Dominica",
39428         "dm",
39429         "1767"
39430       ],
39431       [
39432         "Dominican Republic (República Dominicana)",
39433         "do",
39434         "1",
39435         2,
39436         ["809", "829", "849"]
39437       ],
39438       [
39439         "Ecuador",
39440         "ec",
39441         "593"
39442       ],
39443       [
39444         "Egypt (‫مصر‬‎)",
39445         "eg",
39446         "20"
39447       ],
39448       [
39449         "El Salvador",
39450         "sv",
39451         "503"
39452       ],
39453       [
39454         "Equatorial Guinea (Guinea Ecuatorial)",
39455         "gq",
39456         "240"
39457       ],
39458       [
39459         "Eritrea",
39460         "er",
39461         "291"
39462       ],
39463       [
39464         "Estonia (Eesti)",
39465         "ee",
39466         "372"
39467       ],
39468       [
39469         "Ethiopia",
39470         "et",
39471         "251"
39472       ],
39473       [
39474         "Falkland Islands (Islas Malvinas)",
39475         "fk",
39476         "500"
39477       ],
39478       [
39479         "Faroe Islands (Føroyar)",
39480         "fo",
39481         "298"
39482       ],
39483       [
39484         "Fiji",
39485         "fj",
39486         "679"
39487       ],
39488       [
39489         "Finland (Suomi)",
39490         "fi",
39491         "358",
39492         0
39493       ],
39494       [
39495         "France",
39496         "fr",
39497         "33"
39498       ],
39499       [
39500         "French Guiana (Guyane française)",
39501         "gf",
39502         "594"
39503       ],
39504       [
39505         "French Polynesia (Polynésie française)",
39506         "pf",
39507         "689"
39508       ],
39509       [
39510         "Gabon",
39511         "ga",
39512         "241"
39513       ],
39514       [
39515         "Gambia",
39516         "gm",
39517         "220"
39518       ],
39519       [
39520         "Georgia (საქართველო)",
39521         "ge",
39522         "995"
39523       ],
39524       [
39525         "Germany (Deutschland)",
39526         "de",
39527         "49"
39528       ],
39529       [
39530         "Ghana (Gaana)",
39531         "gh",
39532         "233"
39533       ],
39534       [
39535         "Gibraltar",
39536         "gi",
39537         "350"
39538       ],
39539       [
39540         "Greece (Ελλάδα)",
39541         "gr",
39542         "30"
39543       ],
39544       [
39545         "Greenland (Kalaallit Nunaat)",
39546         "gl",
39547         "299"
39548       ],
39549       [
39550         "Grenada",
39551         "gd",
39552         "1473"
39553       ],
39554       [
39555         "Guadeloupe",
39556         "gp",
39557         "590",
39558         0
39559       ],
39560       [
39561         "Guam",
39562         "gu",
39563         "1671"
39564       ],
39565       [
39566         "Guatemala",
39567         "gt",
39568         "502"
39569       ],
39570       [
39571         "Guernsey",
39572         "gg",
39573         "44",
39574         1
39575       ],
39576       [
39577         "Guinea (Guinée)",
39578         "gn",
39579         "224"
39580       ],
39581       [
39582         "Guinea-Bissau (Guiné Bissau)",
39583         "gw",
39584         "245"
39585       ],
39586       [
39587         "Guyana",
39588         "gy",
39589         "592"
39590       ],
39591       [
39592         "Haiti",
39593         "ht",
39594         "509"
39595       ],
39596       [
39597         "Honduras",
39598         "hn",
39599         "504"
39600       ],
39601       [
39602         "Hong Kong (香港)",
39603         "hk",
39604         "852"
39605       ],
39606       [
39607         "Hungary (Magyarország)",
39608         "hu",
39609         "36"
39610       ],
39611       [
39612         "Iceland (Ísland)",
39613         "is",
39614         "354"
39615       ],
39616       [
39617         "India (भारत)",
39618         "in",
39619         "91"
39620       ],
39621       [
39622         "Indonesia",
39623         "id",
39624         "62"
39625       ],
39626       [
39627         "Iran (‫ایران‬‎)",
39628         "ir",
39629         "98"
39630       ],
39631       [
39632         "Iraq (‫العراق‬‎)",
39633         "iq",
39634         "964"
39635       ],
39636       [
39637         "Ireland",
39638         "ie",
39639         "353"
39640       ],
39641       [
39642         "Isle of Man",
39643         "im",
39644         "44",
39645         2
39646       ],
39647       [
39648         "Israel (‫ישראל‬‎)",
39649         "il",
39650         "972"
39651       ],
39652       [
39653         "Italy (Italia)",
39654         "it",
39655         "39",
39656         0
39657       ],
39658       [
39659         "Jamaica",
39660         "jm",
39661         "1876"
39662       ],
39663       [
39664         "Japan (日本)",
39665         "jp",
39666         "81"
39667       ],
39668       [
39669         "Jersey",
39670         "je",
39671         "44",
39672         3
39673       ],
39674       [
39675         "Jordan (‫الأردن‬‎)",
39676         "jo",
39677         "962"
39678       ],
39679       [
39680         "Kazakhstan (Казахстан)",
39681         "kz",
39682         "7",
39683         1
39684       ],
39685       [
39686         "Kenya",
39687         "ke",
39688         "254"
39689       ],
39690       [
39691         "Kiribati",
39692         "ki",
39693         "686"
39694       ],
39695       [
39696         "Kosovo",
39697         "xk",
39698         "383"
39699       ],
39700       [
39701         "Kuwait (‫الكويت‬‎)",
39702         "kw",
39703         "965"
39704       ],
39705       [
39706         "Kyrgyzstan (Кыргызстан)",
39707         "kg",
39708         "996"
39709       ],
39710       [
39711         "Laos (ລາວ)",
39712         "la",
39713         "856"
39714       ],
39715       [
39716         "Latvia (Latvija)",
39717         "lv",
39718         "371"
39719       ],
39720       [
39721         "Lebanon (‫لبنان‬‎)",
39722         "lb",
39723         "961"
39724       ],
39725       [
39726         "Lesotho",
39727         "ls",
39728         "266"
39729       ],
39730       [
39731         "Liberia",
39732         "lr",
39733         "231"
39734       ],
39735       [
39736         "Libya (‫ليبيا‬‎)",
39737         "ly",
39738         "218"
39739       ],
39740       [
39741         "Liechtenstein",
39742         "li",
39743         "423"
39744       ],
39745       [
39746         "Lithuania (Lietuva)",
39747         "lt",
39748         "370"
39749       ],
39750       [
39751         "Luxembourg",
39752         "lu",
39753         "352"
39754       ],
39755       [
39756         "Macau (澳門)",
39757         "mo",
39758         "853"
39759       ],
39760       [
39761         "Macedonia (FYROM) (Македонија)",
39762         "mk",
39763         "389"
39764       ],
39765       [
39766         "Madagascar (Madagasikara)",
39767         "mg",
39768         "261"
39769       ],
39770       [
39771         "Malawi",
39772         "mw",
39773         "265"
39774       ],
39775       [
39776         "Malaysia",
39777         "my",
39778         "60"
39779       ],
39780       [
39781         "Maldives",
39782         "mv",
39783         "960"
39784       ],
39785       [
39786         "Mali",
39787         "ml",
39788         "223"
39789       ],
39790       [
39791         "Malta",
39792         "mt",
39793         "356"
39794       ],
39795       [
39796         "Marshall Islands",
39797         "mh",
39798         "692"
39799       ],
39800       [
39801         "Martinique",
39802         "mq",
39803         "596"
39804       ],
39805       [
39806         "Mauritania (‫موريتانيا‬‎)",
39807         "mr",
39808         "222"
39809       ],
39810       [
39811         "Mauritius (Moris)",
39812         "mu",
39813         "230"
39814       ],
39815       [
39816         "Mayotte",
39817         "yt",
39818         "262",
39819         1
39820       ],
39821       [
39822         "Mexico (México)",
39823         "mx",
39824         "52"
39825       ],
39826       [
39827         "Micronesia",
39828         "fm",
39829         "691"
39830       ],
39831       [
39832         "Moldova (Republica Moldova)",
39833         "md",
39834         "373"
39835       ],
39836       [
39837         "Monaco",
39838         "mc",
39839         "377"
39840       ],
39841       [
39842         "Mongolia (Монгол)",
39843         "mn",
39844         "976"
39845       ],
39846       [
39847         "Montenegro (Crna Gora)",
39848         "me",
39849         "382"
39850       ],
39851       [
39852         "Montserrat",
39853         "ms",
39854         "1664"
39855       ],
39856       [
39857         "Morocco (‫المغرب‬‎)",
39858         "ma",
39859         "212",
39860         0
39861       ],
39862       [
39863         "Mozambique (Moçambique)",
39864         "mz",
39865         "258"
39866       ],
39867       [
39868         "Myanmar (Burma) (မြန်မာ)",
39869         "mm",
39870         "95"
39871       ],
39872       [
39873         "Namibia (Namibië)",
39874         "na",
39875         "264"
39876       ],
39877       [
39878         "Nauru",
39879         "nr",
39880         "674"
39881       ],
39882       [
39883         "Nepal (नेपाल)",
39884         "np",
39885         "977"
39886       ],
39887       [
39888         "Netherlands (Nederland)",
39889         "nl",
39890         "31"
39891       ],
39892       [
39893         "New Caledonia (Nouvelle-Calédonie)",
39894         "nc",
39895         "687"
39896       ],
39897       [
39898         "New Zealand",
39899         "nz",
39900         "64"
39901       ],
39902       [
39903         "Nicaragua",
39904         "ni",
39905         "505"
39906       ],
39907       [
39908         "Niger (Nijar)",
39909         "ne",
39910         "227"
39911       ],
39912       [
39913         "Nigeria",
39914         "ng",
39915         "234"
39916       ],
39917       [
39918         "Niue",
39919         "nu",
39920         "683"
39921       ],
39922       [
39923         "Norfolk Island",
39924         "nf",
39925         "672"
39926       ],
39927       [
39928         "North Korea (조선 민주주의 인민 공화국)",
39929         "kp",
39930         "850"
39931       ],
39932       [
39933         "Northern Mariana Islands",
39934         "mp",
39935         "1670"
39936       ],
39937       [
39938         "Norway (Norge)",
39939         "no",
39940         "47",
39941         0
39942       ],
39943       [
39944         "Oman (‫عُمان‬‎)",
39945         "om",
39946         "968"
39947       ],
39948       [
39949         "Pakistan (‫پاکستان‬‎)",
39950         "pk",
39951         "92"
39952       ],
39953       [
39954         "Palau",
39955         "pw",
39956         "680"
39957       ],
39958       [
39959         "Palestine (‫فلسطين‬‎)",
39960         "ps",
39961         "970"
39962       ],
39963       [
39964         "Panama (Panamá)",
39965         "pa",
39966         "507"
39967       ],
39968       [
39969         "Papua New Guinea",
39970         "pg",
39971         "675"
39972       ],
39973       [
39974         "Paraguay",
39975         "py",
39976         "595"
39977       ],
39978       [
39979         "Peru (Perú)",
39980         "pe",
39981         "51"
39982       ],
39983       [
39984         "Philippines",
39985         "ph",
39986         "63"
39987       ],
39988       [
39989         "Poland (Polska)",
39990         "pl",
39991         "48"
39992       ],
39993       [
39994         "Portugal",
39995         "pt",
39996         "351"
39997       ],
39998       [
39999         "Puerto Rico",
40000         "pr",
40001         "1",
40002         3,
40003         ["787", "939"]
40004       ],
40005       [
40006         "Qatar (‫قطر‬‎)",
40007         "qa",
40008         "974"
40009       ],
40010       [
40011         "Réunion (La Réunion)",
40012         "re",
40013         "262",
40014         0
40015       ],
40016       [
40017         "Romania (România)",
40018         "ro",
40019         "40"
40020       ],
40021       [
40022         "Russia (Россия)",
40023         "ru",
40024         "7",
40025         0
40026       ],
40027       [
40028         "Rwanda",
40029         "rw",
40030         "250"
40031       ],
40032       [
40033         "Saint Barthélemy",
40034         "bl",
40035         "590",
40036         1
40037       ],
40038       [
40039         "Saint Helena",
40040         "sh",
40041         "290"
40042       ],
40043       [
40044         "Saint Kitts and Nevis",
40045         "kn",
40046         "1869"
40047       ],
40048       [
40049         "Saint Lucia",
40050         "lc",
40051         "1758"
40052       ],
40053       [
40054         "Saint Martin (Saint-Martin (partie française))",
40055         "mf",
40056         "590",
40057         2
40058       ],
40059       [
40060         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40061         "pm",
40062         "508"
40063       ],
40064       [
40065         "Saint Vincent and the Grenadines",
40066         "vc",
40067         "1784"
40068       ],
40069       [
40070         "Samoa",
40071         "ws",
40072         "685"
40073       ],
40074       [
40075         "San Marino",
40076         "sm",
40077         "378"
40078       ],
40079       [
40080         "São Tomé and Príncipe (São Tomé e Príncipe)",
40081         "st",
40082         "239"
40083       ],
40084       [
40085         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
40086         "sa",
40087         "966"
40088       ],
40089       [
40090         "Senegal (Sénégal)",
40091         "sn",
40092         "221"
40093       ],
40094       [
40095         "Serbia (Србија)",
40096         "rs",
40097         "381"
40098       ],
40099       [
40100         "Seychelles",
40101         "sc",
40102         "248"
40103       ],
40104       [
40105         "Sierra Leone",
40106         "sl",
40107         "232"
40108       ],
40109       [
40110         "Singapore",
40111         "sg",
40112         "65"
40113       ],
40114       [
40115         "Sint Maarten",
40116         "sx",
40117         "1721"
40118       ],
40119       [
40120         "Slovakia (Slovensko)",
40121         "sk",
40122         "421"
40123       ],
40124       [
40125         "Slovenia (Slovenija)",
40126         "si",
40127         "386"
40128       ],
40129       [
40130         "Solomon Islands",
40131         "sb",
40132         "677"
40133       ],
40134       [
40135         "Somalia (Soomaaliya)",
40136         "so",
40137         "252"
40138       ],
40139       [
40140         "South Africa",
40141         "za",
40142         "27"
40143       ],
40144       [
40145         "South Korea (대한민국)",
40146         "kr",
40147         "82"
40148       ],
40149       [
40150         "South Sudan (‫جنوب السودان‬‎)",
40151         "ss",
40152         "211"
40153       ],
40154       [
40155         "Spain (España)",
40156         "es",
40157         "34"
40158       ],
40159       [
40160         "Sri Lanka (ශ්‍රී ලංකාව)",
40161         "lk",
40162         "94"
40163       ],
40164       [
40165         "Sudan (‫السودان‬‎)",
40166         "sd",
40167         "249"
40168       ],
40169       [
40170         "Suriname",
40171         "sr",
40172         "597"
40173       ],
40174       [
40175         "Svalbard and Jan Mayen",
40176         "sj",
40177         "47",
40178         1
40179       ],
40180       [
40181         "Swaziland",
40182         "sz",
40183         "268"
40184       ],
40185       [
40186         "Sweden (Sverige)",
40187         "se",
40188         "46"
40189       ],
40190       [
40191         "Switzerland (Schweiz)",
40192         "ch",
40193         "41"
40194       ],
40195       [
40196         "Syria (‫سوريا‬‎)",
40197         "sy",
40198         "963"
40199       ],
40200       [
40201         "Taiwan (台灣)",
40202         "tw",
40203         "886"
40204       ],
40205       [
40206         "Tajikistan",
40207         "tj",
40208         "992"
40209       ],
40210       [
40211         "Tanzania",
40212         "tz",
40213         "255"
40214       ],
40215       [
40216         "Thailand (ไทย)",
40217         "th",
40218         "66"
40219       ],
40220       [
40221         "Timor-Leste",
40222         "tl",
40223         "670"
40224       ],
40225       [
40226         "Togo",
40227         "tg",
40228         "228"
40229       ],
40230       [
40231         "Tokelau",
40232         "tk",
40233         "690"
40234       ],
40235       [
40236         "Tonga",
40237         "to",
40238         "676"
40239       ],
40240       [
40241         "Trinidad and Tobago",
40242         "tt",
40243         "1868"
40244       ],
40245       [
40246         "Tunisia (‫تونس‬‎)",
40247         "tn",
40248         "216"
40249       ],
40250       [
40251         "Turkey (Türkiye)",
40252         "tr",
40253         "90"
40254       ],
40255       [
40256         "Turkmenistan",
40257         "tm",
40258         "993"
40259       ],
40260       [
40261         "Turks and Caicos Islands",
40262         "tc",
40263         "1649"
40264       ],
40265       [
40266         "Tuvalu",
40267         "tv",
40268         "688"
40269       ],
40270       [
40271         "U.S. Virgin Islands",
40272         "vi",
40273         "1340"
40274       ],
40275       [
40276         "Uganda",
40277         "ug",
40278         "256"
40279       ],
40280       [
40281         "Ukraine (Україна)",
40282         "ua",
40283         "380"
40284       ],
40285       [
40286         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40287         "ae",
40288         "971"
40289       ],
40290       [
40291         "United Kingdom",
40292         "gb",
40293         "44",
40294         0
40295       ],
40296       [
40297         "United States",
40298         "us",
40299         "1",
40300         0
40301       ],
40302       [
40303         "Uruguay",
40304         "uy",
40305         "598"
40306       ],
40307       [
40308         "Uzbekistan (Oʻzbekiston)",
40309         "uz",
40310         "998"
40311       ],
40312       [
40313         "Vanuatu",
40314         "vu",
40315         "678"
40316       ],
40317       [
40318         "Vatican City (Città del Vaticano)",
40319         "va",
40320         "39",
40321         1
40322       ],
40323       [
40324         "Venezuela",
40325         "ve",
40326         "58"
40327       ],
40328       [
40329         "Vietnam (Việt Nam)",
40330         "vn",
40331         "84"
40332       ],
40333       [
40334         "Wallis and Futuna (Wallis-et-Futuna)",
40335         "wf",
40336         "681"
40337       ],
40338       [
40339         "Western Sahara (‫الصحراء الغربية‬‎)",
40340         "eh",
40341         "212",
40342         1
40343       ],
40344       [
40345         "Yemen (‫اليمن‬‎)",
40346         "ye",
40347         "967"
40348       ],
40349       [
40350         "Zambia",
40351         "zm",
40352         "260"
40353       ],
40354       [
40355         "Zimbabwe",
40356         "zw",
40357         "263"
40358       ],
40359       [
40360         "Åland Islands",
40361         "ax",
40362         "358",
40363         1
40364       ]
40365   ];
40366   
40367   return d;
40368 }/**
40369 *    This script refer to:
40370 *    Title: International Telephone Input
40371 *    Author: Jack O'Connor
40372 *    Code version:  v12.1.12
40373 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40374 **/
40375
40376 /**
40377  * @class Roo.bootstrap.PhoneInput
40378  * @extends Roo.bootstrap.TriggerField
40379  * An input with International dial-code selection
40380  
40381  * @cfg {String} defaultDialCode default '+852'
40382  * @cfg {Array} preferedCountries default []
40383   
40384  * @constructor
40385  * Create a new PhoneInput.
40386  * @param {Object} config Configuration options
40387  */
40388
40389 Roo.bootstrap.PhoneInput = function(config) {
40390     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40391 };
40392
40393 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40394         
40395         listWidth: undefined,
40396         
40397         selectedClass: 'active',
40398         
40399         invalidClass : "has-warning",
40400         
40401         validClass: 'has-success',
40402         
40403         allowed: '0123456789',
40404         
40405         max_length: 15,
40406         
40407         /**
40408          * @cfg {String} defaultDialCode The default dial code when initializing the input
40409          */
40410         defaultDialCode: '+852',
40411         
40412         /**
40413          * @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
40414          */
40415         preferedCountries: false,
40416         
40417         getAutoCreate : function()
40418         {
40419             var data = Roo.bootstrap.PhoneInputData();
40420             var align = this.labelAlign || this.parentLabelAlign();
40421             var id = Roo.id();
40422             
40423             this.allCountries = [];
40424             this.dialCodeMapping = [];
40425             
40426             for (var i = 0; i < data.length; i++) {
40427               var c = data[i];
40428               this.allCountries[i] = {
40429                 name: c[0],
40430                 iso2: c[1],
40431                 dialCode: c[2],
40432                 priority: c[3] || 0,
40433                 areaCodes: c[4] || null
40434               };
40435               this.dialCodeMapping[c[2]] = {
40436                   name: c[0],
40437                   iso2: c[1],
40438                   priority: c[3] || 0,
40439                   areaCodes: c[4] || null
40440               };
40441             }
40442             
40443             var cfg = {
40444                 cls: 'form-group',
40445                 cn: []
40446             };
40447             
40448             var input =  {
40449                 tag: 'input',
40450                 id : id,
40451                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40452                 maxlength: this.max_length,
40453                 cls : 'form-control tel-input',
40454                 autocomplete: 'new-password'
40455             };
40456             
40457             var hiddenInput = {
40458                 tag: 'input',
40459                 type: 'hidden',
40460                 cls: 'hidden-tel-input'
40461             };
40462             
40463             if (this.name) {
40464                 hiddenInput.name = this.name;
40465             }
40466             
40467             if (this.disabled) {
40468                 input.disabled = true;
40469             }
40470             
40471             var flag_container = {
40472                 tag: 'div',
40473                 cls: 'flag-box',
40474                 cn: [
40475                     {
40476                         tag: 'div',
40477                         cls: 'flag'
40478                     },
40479                     {
40480                         tag: 'div',
40481                         cls: 'caret'
40482                     }
40483                 ]
40484             };
40485             
40486             var box = {
40487                 tag: 'div',
40488                 cls: this.hasFeedback ? 'has-feedback' : '',
40489                 cn: [
40490                     hiddenInput,
40491                     input,
40492                     {
40493                         tag: 'input',
40494                         cls: 'dial-code-holder',
40495                         disabled: true
40496                     }
40497                 ]
40498             };
40499             
40500             var container = {
40501                 cls: 'roo-select2-container input-group',
40502                 cn: [
40503                     flag_container,
40504                     box
40505                 ]
40506             };
40507             
40508             if (this.fieldLabel.length) {
40509                 var indicator = {
40510                     tag: 'i',
40511                     tooltip: 'This field is required'
40512                 };
40513                 
40514                 var label = {
40515                     tag: 'label',
40516                     'for':  id,
40517                     cls: 'control-label',
40518                     cn: []
40519                 };
40520                 
40521                 var label_text = {
40522                     tag: 'span',
40523                     html: this.fieldLabel
40524                 };
40525                 
40526                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40527                 label.cn = [
40528                     indicator,
40529                     label_text
40530                 ];
40531                 
40532                 if(this.indicatorpos == 'right') {
40533                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40534                     label.cn = [
40535                         label_text,
40536                         indicator
40537                     ];
40538                 }
40539                 
40540                 if(align == 'left') {
40541                     container = {
40542                         tag: 'div',
40543                         cn: [
40544                             container
40545                         ]
40546                     };
40547                     
40548                     if(this.labelWidth > 12){
40549                         label.style = "width: " + this.labelWidth + 'px';
40550                     }
40551                     if(this.labelWidth < 13 && this.labelmd == 0){
40552                         this.labelmd = this.labelWidth;
40553                     }
40554                     if(this.labellg > 0){
40555                         label.cls += ' col-lg-' + this.labellg;
40556                         input.cls += ' col-lg-' + (12 - this.labellg);
40557                     }
40558                     if(this.labelmd > 0){
40559                         label.cls += ' col-md-' + this.labelmd;
40560                         container.cls += ' col-md-' + (12 - this.labelmd);
40561                     }
40562                     if(this.labelsm > 0){
40563                         label.cls += ' col-sm-' + this.labelsm;
40564                         container.cls += ' col-sm-' + (12 - this.labelsm);
40565                     }
40566                     if(this.labelxs > 0){
40567                         label.cls += ' col-xs-' + this.labelxs;
40568                         container.cls += ' col-xs-' + (12 - this.labelxs);
40569                     }
40570                 }
40571             }
40572             
40573             cfg.cn = [
40574                 label,
40575                 container
40576             ];
40577             
40578             var settings = this;
40579             
40580             ['xs','sm','md','lg'].map(function(size){
40581                 if (settings[size]) {
40582                     cfg.cls += ' col-' + size + '-' + settings[size];
40583                 }
40584             });
40585             
40586             this.store = new Roo.data.Store({
40587                 proxy : new Roo.data.MemoryProxy({}),
40588                 reader : new Roo.data.JsonReader({
40589                     fields : [
40590                         {
40591                             'name' : 'name',
40592                             'type' : 'string'
40593                         },
40594                         {
40595                             'name' : 'iso2',
40596                             'type' : 'string'
40597                         },
40598                         {
40599                             'name' : 'dialCode',
40600                             'type' : 'string'
40601                         },
40602                         {
40603                             'name' : 'priority',
40604                             'type' : 'string'
40605                         },
40606                         {
40607                             'name' : 'areaCodes',
40608                             'type' : 'string'
40609                         }
40610                     ]
40611                 })
40612             });
40613             
40614             if(!this.preferedCountries) {
40615                 this.preferedCountries = [
40616                     'hk',
40617                     'gb',
40618                     'us'
40619                 ];
40620             }
40621             
40622             var p = this.preferedCountries.reverse();
40623             
40624             if(p) {
40625                 for (var i = 0; i < p.length; i++) {
40626                     for (var j = 0; j < this.allCountries.length; j++) {
40627                         if(this.allCountries[j].iso2 == p[i]) {
40628                             var t = this.allCountries[j];
40629                             this.allCountries.splice(j,1);
40630                             this.allCountries.unshift(t);
40631                         }
40632                     } 
40633                 }
40634             }
40635             
40636             this.store.proxy.data = {
40637                 success: true,
40638                 data: this.allCountries
40639             };
40640             
40641             return cfg;
40642         },
40643         
40644         initEvents : function()
40645         {
40646             this.createList();
40647             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40648             
40649             this.indicator = this.indicatorEl();
40650             this.flag = this.flagEl();
40651             this.dialCodeHolder = this.dialCodeHolderEl();
40652             
40653             this.trigger = this.el.select('div.flag-box',true).first();
40654             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40655             
40656             var _this = this;
40657             
40658             (function(){
40659                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40660                 _this.list.setWidth(lw);
40661             }).defer(100);
40662             
40663             this.list.on('mouseover', this.onViewOver, this);
40664             this.list.on('mousemove', this.onViewMove, this);
40665             this.inputEl().on("keyup", this.onKeyUp, this);
40666             this.inputEl().on("keypress", this.onKeyPress, this);
40667             
40668             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40669
40670             this.view = new Roo.View(this.list, this.tpl, {
40671                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40672             });
40673             
40674             this.view.on('click', this.onViewClick, this);
40675             this.setValue(this.defaultDialCode);
40676         },
40677         
40678         onTriggerClick : function(e)
40679         {
40680             Roo.log('trigger click');
40681             if(this.disabled){
40682                 return;
40683             }
40684             
40685             if(this.isExpanded()){
40686                 this.collapse();
40687                 this.hasFocus = false;
40688             }else {
40689                 this.store.load({});
40690                 this.hasFocus = true;
40691                 this.expand();
40692             }
40693         },
40694         
40695         isExpanded : function()
40696         {
40697             return this.list.isVisible();
40698         },
40699         
40700         collapse : function()
40701         {
40702             if(!this.isExpanded()){
40703                 return;
40704             }
40705             this.list.hide();
40706             Roo.get(document).un('mousedown', this.collapseIf, this);
40707             Roo.get(document).un('mousewheel', this.collapseIf, this);
40708             this.fireEvent('collapse', this);
40709             this.validate();
40710         },
40711         
40712         expand : function()
40713         {
40714             Roo.log('expand');
40715
40716             if(this.isExpanded() || !this.hasFocus){
40717                 return;
40718             }
40719             
40720             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40721             this.list.setWidth(lw);
40722             
40723             this.list.show();
40724             this.restrictHeight();
40725             
40726             Roo.get(document).on('mousedown', this.collapseIf, this);
40727             Roo.get(document).on('mousewheel', this.collapseIf, this);
40728             
40729             this.fireEvent('expand', this);
40730         },
40731         
40732         restrictHeight : function()
40733         {
40734             this.list.alignTo(this.inputEl(), this.listAlign);
40735             this.list.alignTo(this.inputEl(), this.listAlign);
40736         },
40737         
40738         onViewOver : function(e, t)
40739         {
40740             if(this.inKeyMode){
40741                 return;
40742             }
40743             var item = this.view.findItemFromChild(t);
40744             
40745             if(item){
40746                 var index = this.view.indexOf(item);
40747                 this.select(index, false);
40748             }
40749         },
40750
40751         // private
40752         onViewClick : function(view, doFocus, el, e)
40753         {
40754             var index = this.view.getSelectedIndexes()[0];
40755             
40756             var r = this.store.getAt(index);
40757             
40758             if(r){
40759                 this.onSelect(r, index);
40760             }
40761             if(doFocus !== false && !this.blockFocus){
40762                 this.inputEl().focus();
40763             }
40764         },
40765         
40766         onViewMove : function(e, t)
40767         {
40768             this.inKeyMode = false;
40769         },
40770         
40771         select : function(index, scrollIntoView)
40772         {
40773             this.selectedIndex = index;
40774             this.view.select(index);
40775             if(scrollIntoView !== false){
40776                 var el = this.view.getNode(index);
40777                 if(el){
40778                     this.list.scrollChildIntoView(el, false);
40779                 }
40780             }
40781         },
40782         
40783         createList : function()
40784         {
40785             this.list = Roo.get(document.body).createChild({
40786                 tag: 'ul',
40787                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40788                 style: 'display:none'
40789             });
40790             
40791             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40792         },
40793         
40794         collapseIf : function(e)
40795         {
40796             var in_combo  = e.within(this.el);
40797             var in_list =  e.within(this.list);
40798             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40799             
40800             if (in_combo || in_list || is_list) {
40801                 return;
40802             }
40803             this.collapse();
40804         },
40805         
40806         onSelect : function(record, index)
40807         {
40808             if(this.fireEvent('beforeselect', this, record, index) !== false){
40809                 
40810                 this.setFlagClass(record.data.iso2);
40811                 this.setDialCode(record.data.dialCode);
40812                 this.hasFocus = false;
40813                 this.collapse();
40814                 this.fireEvent('select', this, record, index);
40815             }
40816         },
40817         
40818         flagEl : function()
40819         {
40820             var flag = this.el.select('div.flag',true).first();
40821             if(!flag){
40822                 return false;
40823             }
40824             return flag;
40825         },
40826         
40827         dialCodeHolderEl : function()
40828         {
40829             var d = this.el.select('input.dial-code-holder',true).first();
40830             if(!d){
40831                 return false;
40832             }
40833             return d;
40834         },
40835         
40836         setDialCode : function(v)
40837         {
40838             this.dialCodeHolder.dom.value = '+'+v;
40839         },
40840         
40841         setFlagClass : function(n)
40842         {
40843             this.flag.dom.className = 'flag '+n;
40844         },
40845         
40846         getValue : function()
40847         {
40848             var v = this.inputEl().getValue();
40849             if(this.dialCodeHolder) {
40850                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40851             }
40852             return v;
40853         },
40854         
40855         setValue : function(v)
40856         {
40857             var d = this.getDialCode(v);
40858             
40859             //invalid dial code
40860             if(v.length == 0 || !d || d.length == 0) {
40861                 if(this.rendered){
40862                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40863                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40864                 }
40865                 return;
40866             }
40867             
40868             //valid dial code
40869             this.setFlagClass(this.dialCodeMapping[d].iso2);
40870             this.setDialCode(d);
40871             this.inputEl().dom.value = v.replace('+'+d,'');
40872             this.hiddenEl().dom.value = this.getValue();
40873             
40874             this.validate();
40875         },
40876         
40877         getDialCode : function(v)
40878         {
40879             v = v ||  '';
40880             
40881             if (v.length == 0) {
40882                 return this.dialCodeHolder.dom.value;
40883             }
40884             
40885             var dialCode = "";
40886             if (v.charAt(0) != "+") {
40887                 return false;
40888             }
40889             var numericChars = "";
40890             for (var i = 1; i < v.length; i++) {
40891               var c = v.charAt(i);
40892               if (!isNaN(c)) {
40893                 numericChars += c;
40894                 if (this.dialCodeMapping[numericChars]) {
40895                   dialCode = v.substr(1, i);
40896                 }
40897                 if (numericChars.length == 4) {
40898                   break;
40899                 }
40900               }
40901             }
40902             return dialCode;
40903         },
40904         
40905         reset : function()
40906         {
40907             this.setValue(this.defaultDialCode);
40908             this.validate();
40909         },
40910         
40911         hiddenEl : function()
40912         {
40913             return this.el.select('input.hidden-tel-input',true).first();
40914         },
40915         
40916         // after setting val
40917         onKeyUp : function(e){
40918             this.setValue(this.getValue());
40919         },
40920         
40921         onKeyPress : function(e){
40922             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40923                 e.stopEvent();
40924             }
40925         }
40926         
40927 });
40928 /**
40929  * @class Roo.bootstrap.MoneyField
40930  * @extends Roo.bootstrap.ComboBox
40931  * Bootstrap MoneyField class
40932  * 
40933  * @constructor
40934  * Create a new MoneyField.
40935  * @param {Object} config Configuration options
40936  */
40937
40938 Roo.bootstrap.MoneyField = function(config) {
40939     
40940     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40941     
40942 };
40943
40944 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40945     
40946     /**
40947      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40948      */
40949     allowDecimals : true,
40950     /**
40951      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40952      */
40953     decimalSeparator : ".",
40954     /**
40955      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40956      */
40957     decimalPrecision : 0,
40958     /**
40959      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40960      */
40961     allowNegative : true,
40962     /**
40963      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40964      */
40965     allowZero: true,
40966     /**
40967      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40968      */
40969     minValue : Number.NEGATIVE_INFINITY,
40970     /**
40971      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40972      */
40973     maxValue : Number.MAX_VALUE,
40974     /**
40975      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40976      */
40977     minText : "The minimum value for this field is {0}",
40978     /**
40979      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40980      */
40981     maxText : "The maximum value for this field is {0}",
40982     /**
40983      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40984      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40985      */
40986     nanText : "{0} is not a valid number",
40987     /**
40988      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40989      */
40990     castInt : true,
40991     /**
40992      * @cfg {String} defaults currency of the MoneyField
40993      * value should be in lkey
40994      */
40995     defaultCurrency : false,
40996     /**
40997      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40998      */
40999     thousandsDelimiter : false,
41000     /**
41001      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
41002      */
41003     max_length: false,
41004     
41005     inputlg : 9,
41006     inputmd : 9,
41007     inputsm : 9,
41008     inputxs : 6,
41009     
41010     store : false,
41011     
41012     getAutoCreate : function()
41013     {
41014         var align = this.labelAlign || this.parentLabelAlign();
41015         
41016         var id = Roo.id();
41017
41018         var cfg = {
41019             cls: 'form-group',
41020             cn: []
41021         };
41022
41023         var input =  {
41024             tag: 'input',
41025             id : id,
41026             cls : 'form-control roo-money-amount-input',
41027             autocomplete: 'new-password'
41028         };
41029         
41030         var hiddenInput = {
41031             tag: 'input',
41032             type: 'hidden',
41033             id: Roo.id(),
41034             cls: 'hidden-number-input'
41035         };
41036         
41037         if(this.max_length) {
41038             input.maxlength = this.max_length; 
41039         }
41040         
41041         if (this.name) {
41042             hiddenInput.name = this.name;
41043         }
41044
41045         if (this.disabled) {
41046             input.disabled = true;
41047         }
41048
41049         var clg = 12 - this.inputlg;
41050         var cmd = 12 - this.inputmd;
41051         var csm = 12 - this.inputsm;
41052         var cxs = 12 - this.inputxs;
41053         
41054         var container = {
41055             tag : 'div',
41056             cls : 'row roo-money-field',
41057             cn : [
41058                 {
41059                     tag : 'div',
41060                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41061                     cn : [
41062                         {
41063                             tag : 'div',
41064                             cls: 'roo-select2-container input-group',
41065                             cn: [
41066                                 {
41067                                     tag : 'input',
41068                                     cls : 'form-control roo-money-currency-input',
41069                                     autocomplete: 'new-password',
41070                                     readOnly : 1,
41071                                     name : this.currencyName
41072                                 },
41073                                 {
41074                                     tag :'span',
41075                                     cls : 'input-group-addon',
41076                                     cn : [
41077                                         {
41078                                             tag: 'span',
41079                                             cls: 'caret'
41080                                         }
41081                                     ]
41082                                 }
41083                             ]
41084                         }
41085                     ]
41086                 },
41087                 {
41088                     tag : 'div',
41089                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41090                     cn : [
41091                         {
41092                             tag: 'div',
41093                             cls: this.hasFeedback ? 'has-feedback' : '',
41094                             cn: [
41095                                 input
41096                             ]
41097                         }
41098                     ]
41099                 }
41100             ]
41101             
41102         };
41103         
41104         if (this.fieldLabel.length) {
41105             var indicator = {
41106                 tag: 'i',
41107                 tooltip: 'This field is required'
41108             };
41109
41110             var label = {
41111                 tag: 'label',
41112                 'for':  id,
41113                 cls: 'control-label',
41114                 cn: []
41115             };
41116
41117             var label_text = {
41118                 tag: 'span',
41119                 html: this.fieldLabel
41120             };
41121
41122             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41123             label.cn = [
41124                 indicator,
41125                 label_text
41126             ];
41127
41128             if(this.indicatorpos == 'right') {
41129                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41130                 label.cn = [
41131                     label_text,
41132                     indicator
41133                 ];
41134             }
41135
41136             if(align == 'left') {
41137                 container = {
41138                     tag: 'div',
41139                     cn: [
41140                         container
41141                     ]
41142                 };
41143
41144                 if(this.labelWidth > 12){
41145                     label.style = "width: " + this.labelWidth + 'px';
41146                 }
41147                 if(this.labelWidth < 13 && this.labelmd == 0){
41148                     this.labelmd = this.labelWidth;
41149                 }
41150                 if(this.labellg > 0){
41151                     label.cls += ' col-lg-' + this.labellg;
41152                     input.cls += ' col-lg-' + (12 - this.labellg);
41153                 }
41154                 if(this.labelmd > 0){
41155                     label.cls += ' col-md-' + this.labelmd;
41156                     container.cls += ' col-md-' + (12 - this.labelmd);
41157                 }
41158                 if(this.labelsm > 0){
41159                     label.cls += ' col-sm-' + this.labelsm;
41160                     container.cls += ' col-sm-' + (12 - this.labelsm);
41161                 }
41162                 if(this.labelxs > 0){
41163                     label.cls += ' col-xs-' + this.labelxs;
41164                     container.cls += ' col-xs-' + (12 - this.labelxs);
41165                 }
41166             }
41167         }
41168
41169         cfg.cn = [
41170             label,
41171             container,
41172             hiddenInput
41173         ];
41174         
41175         var settings = this;
41176
41177         ['xs','sm','md','lg'].map(function(size){
41178             if (settings[size]) {
41179                 cfg.cls += ' col-' + size + '-' + settings[size];
41180             }
41181         });
41182         
41183         return cfg;
41184     },
41185     
41186     initEvents : function()
41187     {
41188         this.indicator = this.indicatorEl();
41189         
41190         this.initCurrencyEvent();
41191         
41192         this.initNumberEvent();
41193     },
41194     
41195     initCurrencyEvent : function()
41196     {
41197         if (!this.store) {
41198             throw "can not find store for combo";
41199         }
41200         
41201         this.store = Roo.factory(this.store, Roo.data);
41202         this.store.parent = this;
41203         
41204         this.createList();
41205         
41206         this.triggerEl = this.el.select('.input-group-addon', true).first();
41207         
41208         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41209         
41210         var _this = this;
41211         
41212         (function(){
41213             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41214             _this.list.setWidth(lw);
41215         }).defer(100);
41216         
41217         this.list.on('mouseover', this.onViewOver, this);
41218         this.list.on('mousemove', this.onViewMove, this);
41219         this.list.on('scroll', this.onViewScroll, this);
41220         
41221         if(!this.tpl){
41222             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41223         }
41224         
41225         this.view = new Roo.View(this.list, this.tpl, {
41226             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41227         });
41228         
41229         this.view.on('click', this.onViewClick, this);
41230         
41231         this.store.on('beforeload', this.onBeforeLoad, this);
41232         this.store.on('load', this.onLoad, this);
41233         this.store.on('loadexception', this.onLoadException, this);
41234         
41235         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41236             "up" : function(e){
41237                 this.inKeyMode = true;
41238                 this.selectPrev();
41239             },
41240
41241             "down" : function(e){
41242                 if(!this.isExpanded()){
41243                     this.onTriggerClick();
41244                 }else{
41245                     this.inKeyMode = true;
41246                     this.selectNext();
41247                 }
41248             },
41249
41250             "enter" : function(e){
41251                 this.collapse();
41252                 
41253                 if(this.fireEvent("specialkey", this, e)){
41254                     this.onViewClick(false);
41255                 }
41256                 
41257                 return true;
41258             },
41259
41260             "esc" : function(e){
41261                 this.collapse();
41262             },
41263
41264             "tab" : function(e){
41265                 this.collapse();
41266                 
41267                 if(this.fireEvent("specialkey", this, e)){
41268                     this.onViewClick(false);
41269                 }
41270                 
41271                 return true;
41272             },
41273
41274             scope : this,
41275
41276             doRelay : function(foo, bar, hname){
41277                 if(hname == 'down' || this.scope.isExpanded()){
41278                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41279                 }
41280                 return true;
41281             },
41282
41283             forceKeyDown: true
41284         });
41285         
41286         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41287         
41288     },
41289     
41290     initNumberEvent : function(e)
41291     {
41292         this.inputEl().on("keydown" , this.fireKey,  this);
41293         this.inputEl().on("focus", this.onFocus,  this);
41294         this.inputEl().on("blur", this.onBlur,  this);
41295         
41296         this.inputEl().relayEvent('keyup', this);
41297         
41298         if(this.indicator){
41299             this.indicator.addClass('invisible');
41300         }
41301  
41302         this.originalValue = this.getValue();
41303         
41304         if(this.validationEvent == 'keyup'){
41305             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41306             this.inputEl().on('keyup', this.filterValidation, this);
41307         }
41308         else if(this.validationEvent !== false){
41309             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41310         }
41311         
41312         if(this.selectOnFocus){
41313             this.on("focus", this.preFocus, this);
41314             
41315         }
41316         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41317             this.inputEl().on("keypress", this.filterKeys, this);
41318         } else {
41319             this.inputEl().relayEvent('keypress', this);
41320         }
41321         
41322         var allowed = "0123456789";
41323         
41324         if(this.allowDecimals){
41325             allowed += this.decimalSeparator;
41326         }
41327         
41328         if(this.allowNegative){
41329             allowed += "-";
41330         }
41331         
41332         if(this.thousandsDelimiter) {
41333             allowed += ",";
41334         }
41335         
41336         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41337         
41338         var keyPress = function(e){
41339             
41340             var k = e.getKey();
41341             
41342             var c = e.getCharCode();
41343             
41344             if(
41345                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41346                     allowed.indexOf(String.fromCharCode(c)) === -1
41347             ){
41348                 e.stopEvent();
41349                 return;
41350             }
41351             
41352             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41353                 return;
41354             }
41355             
41356             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41357                 e.stopEvent();
41358             }
41359         };
41360         
41361         this.inputEl().on("keypress", keyPress, this);
41362         
41363     },
41364     
41365     onTriggerClick : function(e)
41366     {   
41367         if(this.disabled){
41368             return;
41369         }
41370         
41371         this.page = 0;
41372         this.loadNext = false;
41373         
41374         if(this.isExpanded()){
41375             this.collapse();
41376             return;
41377         }
41378         
41379         this.hasFocus = true;
41380         
41381         if(this.triggerAction == 'all') {
41382             this.doQuery(this.allQuery, true);
41383             return;
41384         }
41385         
41386         this.doQuery(this.getRawValue());
41387     },
41388     
41389     getCurrency : function()
41390     {   
41391         var v = this.currencyEl().getValue();
41392         
41393         return v;
41394     },
41395     
41396     restrictHeight : function()
41397     {
41398         this.list.alignTo(this.currencyEl(), this.listAlign);
41399         this.list.alignTo(this.currencyEl(), this.listAlign);
41400     },
41401     
41402     onViewClick : function(view, doFocus, el, e)
41403     {
41404         var index = this.view.getSelectedIndexes()[0];
41405         
41406         var r = this.store.getAt(index);
41407         
41408         if(r){
41409             this.onSelect(r, index);
41410         }
41411     },
41412     
41413     onSelect : function(record, index){
41414         
41415         if(this.fireEvent('beforeselect', this, record, index) !== false){
41416         
41417             this.setFromCurrencyData(index > -1 ? record.data : false);
41418             
41419             this.collapse();
41420             
41421             this.fireEvent('select', this, record, index);
41422         }
41423     },
41424     
41425     setFromCurrencyData : function(o)
41426     {
41427         var currency = '';
41428         
41429         this.lastCurrency = o;
41430         
41431         if (this.currencyField) {
41432             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41433         } else {
41434             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41435         }
41436         
41437         this.lastSelectionText = currency;
41438         
41439         //setting default currency
41440         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41441             this.setCurrency(this.defaultCurrency);
41442             return;
41443         }
41444         
41445         this.setCurrency(currency);
41446     },
41447     
41448     setFromData : function(o)
41449     {
41450         var c = {};
41451         
41452         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41453         
41454         this.setFromCurrencyData(c);
41455         
41456         var value = '';
41457         
41458         if (this.name) {
41459             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41460         } else {
41461             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41462         }
41463         
41464         this.setValue(value);
41465         
41466     },
41467     
41468     setCurrency : function(v)
41469     {   
41470         this.currencyValue = v;
41471         
41472         if(this.rendered){
41473             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41474             this.validate();
41475         }
41476     },
41477     
41478     setValue : function(v)
41479     {
41480         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41481         
41482         this.value = v;
41483         
41484         if(this.rendered){
41485             
41486             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41487             
41488             this.inputEl().dom.value = (v == '') ? '' :
41489                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41490             
41491             if(!this.allowZero && v === '0') {
41492                 this.hiddenEl().dom.value = '';
41493                 this.inputEl().dom.value = '';
41494             }
41495             
41496             this.validate();
41497         }
41498     },
41499     
41500     getRawValue : function()
41501     {
41502         var v = this.inputEl().getValue();
41503         
41504         return v;
41505     },
41506     
41507     getValue : function()
41508     {
41509         return this.fixPrecision(this.parseValue(this.getRawValue()));
41510     },
41511     
41512     parseValue : function(value)
41513     {
41514         if(this.thousandsDelimiter) {
41515             value += "";
41516             r = new RegExp(",", "g");
41517             value = value.replace(r, "");
41518         }
41519         
41520         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41521         return isNaN(value) ? '' : value;
41522         
41523     },
41524     
41525     fixPrecision : function(value)
41526     {
41527         if(this.thousandsDelimiter) {
41528             value += "";
41529             r = new RegExp(",", "g");
41530             value = value.replace(r, "");
41531         }
41532         
41533         var nan = isNaN(value);
41534         
41535         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41536             return nan ? '' : value;
41537         }
41538         return parseFloat(value).toFixed(this.decimalPrecision);
41539     },
41540     
41541     decimalPrecisionFcn : function(v)
41542     {
41543         return Math.floor(v);
41544     },
41545     
41546     validateValue : function(value)
41547     {
41548         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41549             return false;
41550         }
41551         
41552         var num = this.parseValue(value);
41553         
41554         if(isNaN(num)){
41555             this.markInvalid(String.format(this.nanText, value));
41556             return false;
41557         }
41558         
41559         if(num < this.minValue){
41560             this.markInvalid(String.format(this.minText, this.minValue));
41561             return false;
41562         }
41563         
41564         if(num > this.maxValue){
41565             this.markInvalid(String.format(this.maxText, this.maxValue));
41566             return false;
41567         }
41568         
41569         return true;
41570     },
41571     
41572     validate : function()
41573     {
41574         if(this.disabled || this.allowBlank){
41575             this.markValid();
41576             return true;
41577         }
41578         
41579         var currency = this.getCurrency();
41580         
41581         if(this.validateValue(this.getRawValue()) && currency.length){
41582             this.markValid();
41583             return true;
41584         }
41585         
41586         this.markInvalid();
41587         return false;
41588     },
41589     
41590     getName: function()
41591     {
41592         return this.name;
41593     },
41594     
41595     beforeBlur : function()
41596     {
41597         if(!this.castInt){
41598             return;
41599         }
41600         
41601         var v = this.parseValue(this.getRawValue());
41602         
41603         if(v || v == 0){
41604             this.setValue(v);
41605         }
41606     },
41607     
41608     onBlur : function()
41609     {
41610         this.beforeBlur();
41611         
41612         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41613             //this.el.removeClass(this.focusClass);
41614         }
41615         
41616         this.hasFocus = false;
41617         
41618         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41619             this.validate();
41620         }
41621         
41622         var v = this.getValue();
41623         
41624         if(String(v) !== String(this.startValue)){
41625             this.fireEvent('change', this, v, this.startValue);
41626         }
41627         
41628         this.fireEvent("blur", this);
41629     },
41630     
41631     inputEl : function()
41632     {
41633         return this.el.select('.roo-money-amount-input', true).first();
41634     },
41635     
41636     currencyEl : function()
41637     {
41638         return this.el.select('.roo-money-currency-input', true).first();
41639     },
41640     
41641     hiddenEl : function()
41642     {
41643         return this.el.select('input.hidden-number-input',true).first();
41644     }
41645     
41646 });/**
41647  * @class Roo.bootstrap.BezierSignature
41648  * @extends Roo.bootstrap.Component
41649  * Bootstrap BezierSignature class
41650  * This script refer to:
41651  *    Title: Signature Pad
41652  *    Author: szimek
41653  *    Availability: https://github.com/szimek/signature_pad
41654  *
41655  * @constructor
41656  * Create a new BezierSignature
41657  * @param {Object} config The config object
41658  */
41659
41660 Roo.bootstrap.BezierSignature = function(config){
41661     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41662     this.addEvents({
41663         "resize" : true
41664     });
41665 };
41666
41667 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41668 {
41669      
41670     curve_data: [],
41671     
41672     is_empty: true,
41673     
41674     mouse_btn_down: true,
41675     
41676     /**
41677      * @cfg {int} canvas height
41678      */
41679     canvas_height: '200px',
41680     
41681     /**
41682      * @cfg {float|function} Radius of a single dot.
41683      */ 
41684     dot_size: false,
41685     
41686     /**
41687      * @cfg {float} Minimum width of a line. Defaults to 0.5.
41688      */
41689     min_width: 0.5,
41690     
41691     /**
41692      * @cfg {float} Maximum width of a line. Defaults to 2.5.
41693      */
41694     max_width: 2.5,
41695     
41696     /**
41697      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41698      */
41699     throttle: 16,
41700     
41701     /**
41702      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41703      */
41704     min_distance: 5,
41705     
41706     /**
41707      * @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.
41708      */
41709     bg_color: 'rgba(0, 0, 0, 0)',
41710     
41711     /**
41712      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41713      */
41714     dot_color: 'black',
41715     
41716     /**
41717      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41718      */ 
41719     velocity_filter_weight: 0.7,
41720     
41721     /**
41722      * @cfg {function} Callback when stroke begin. 
41723      */
41724     onBegin: false,
41725     
41726     /**
41727      * @cfg {function} Callback when stroke end.
41728      */
41729     onEnd: false,
41730     
41731     getAutoCreate : function()
41732     {
41733         var cls = 'roo-signature column';
41734         
41735         if(this.cls){
41736             cls += ' ' + this.cls;
41737         }
41738         
41739         var col_sizes = [
41740             'lg',
41741             'md',
41742             'sm',
41743             'xs'
41744         ];
41745         
41746         for(var i = 0; i < col_sizes.length; i++) {
41747             if(this[col_sizes[i]]) {
41748                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
41749             }
41750         }
41751         
41752         var cfg = {
41753             tag: 'div',
41754             cls: cls,
41755             cn: [
41756                 {
41757                     tag: 'div',
41758                     cls: 'roo-signature-body',
41759                     cn: [
41760                         {
41761                             tag: 'canvas',
41762                             cls: 'roo-signature-body-canvas',
41763                             height: this.canvas_height,
41764                             width: this.canvas_width
41765                         }
41766                     ]
41767                 },
41768                 {
41769                     tag: 'input',
41770                     type: 'file',
41771                     style: 'display: none'
41772                 }
41773             ]
41774         };
41775         
41776         return cfg;
41777     },
41778     
41779     initEvents: function() 
41780     {
41781         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
41782         
41783         var canvas = this.canvasEl();
41784         
41785         // mouse && touch event swapping...
41786         canvas.dom.style.touchAction = 'none';
41787         canvas.dom.style.msTouchAction = 'none';
41788         
41789         this.mouse_btn_down = false;
41790         canvas.on('mousedown', this._handleMouseDown, this);
41791         canvas.on('mousemove', this._handleMouseMove, this);
41792         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
41793         
41794         if (window.PointerEvent) {
41795             canvas.on('pointerdown', this._handleMouseDown, this);
41796             canvas.on('pointermove', this._handleMouseMove, this);
41797             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
41798         }
41799         
41800         if ('ontouchstart' in window) {
41801             canvas.on('touchstart', this._handleTouchStart, this);
41802             canvas.on('touchmove', this._handleTouchMove, this);
41803             canvas.on('touchend', this._handleTouchEnd, this);
41804         }
41805         
41806         Roo.EventManager.onWindowResize(this.resize, this, true);
41807         
41808         // file input event
41809         this.fileEl().on('change', this.uploadImage, this);
41810         
41811         this.clear();
41812         
41813         this.resize();
41814     },
41815     
41816     resize: function(){
41817         
41818         var canvas = this.canvasEl().dom;
41819         var ctx = this.canvasElCtx();
41820         var img_data = false;
41821         
41822         if(canvas.width > 0) {
41823             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
41824         }
41825         // setting canvas width will clean img data
41826         canvas.width = 0;
41827         
41828         var style = window.getComputedStyle ? 
41829             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
41830             
41831         var padding_left = parseInt(style.paddingLeft) || 0;
41832         var padding_right = parseInt(style.paddingRight) || 0;
41833         
41834         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
41835         
41836         if(img_data) {
41837             ctx.putImageData(img_data, 0, 0);
41838         }
41839     },
41840     
41841     _handleMouseDown: function(e)
41842     {
41843         if (e.browserEvent.which === 1) {
41844             this.mouse_btn_down = true;
41845             this.strokeBegin(e);
41846         }
41847     },
41848     
41849     _handleMouseMove: function (e)
41850     {
41851         if (this.mouse_btn_down) {
41852             this.strokeMoveUpdate(e);
41853         }
41854     },
41855     
41856     _handleMouseUp: function (e)
41857     {
41858         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
41859             this.mouse_btn_down = false;
41860             this.strokeEnd(e);
41861         }
41862     },
41863     
41864     _handleTouchStart: function (e) {
41865         
41866         e.preventDefault();
41867         if (e.browserEvent.targetTouches.length === 1) {
41868             // var touch = e.browserEvent.changedTouches[0];
41869             // this.strokeBegin(touch);
41870             
41871              this.strokeBegin(e); // assume e catching the correct xy...
41872         }
41873     },
41874     
41875     _handleTouchMove: function (e) {
41876         e.preventDefault();
41877         // var touch = event.targetTouches[0];
41878         // _this._strokeMoveUpdate(touch);
41879         this.strokeMoveUpdate(e);
41880     },
41881     
41882     _handleTouchEnd: function (e) {
41883         var wasCanvasTouched = e.target === this.canvasEl().dom;
41884         if (wasCanvasTouched) {
41885             e.preventDefault();
41886             // var touch = event.changedTouches[0];
41887             // _this._strokeEnd(touch);
41888             this.strokeEnd(e);
41889         }
41890     },
41891     
41892     reset: function () {
41893         this._lastPoints = [];
41894         this._lastVelocity = 0;
41895         this._lastWidth = (this.min_width + this.max_width) / 2;
41896         this.canvasElCtx().fillStyle = this.dot_color;
41897     },
41898     
41899     strokeMoveUpdate: function(e)
41900     {
41901         this.strokeUpdate(e);
41902         
41903         if (this.throttle) {
41904             this.throttleStroke(this.strokeUpdate, this.throttle);
41905         }
41906         else {
41907             this.strokeUpdate(e);
41908         }
41909     },
41910     
41911     strokeBegin: function(e)
41912     {
41913         var newPointGroup = {
41914             color: this.dot_color,
41915             points: []
41916         };
41917         
41918         if (typeof this.onBegin === 'function') {
41919             this.onBegin(e);
41920         }
41921         
41922         this.curve_data.push(newPointGroup);
41923         this.reset();
41924         this.strokeUpdate(e);
41925     },
41926     
41927     strokeUpdate: function(e)
41928     {
41929         var rect = this.canvasEl().dom.getBoundingClientRect();
41930         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
41931         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
41932         var lastPoints = lastPointGroup.points;
41933         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
41934         var isLastPointTooClose = lastPoint
41935             ? point.distanceTo(lastPoint) <= this.min_distance
41936             : false;
41937         var color = lastPointGroup.color;
41938         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
41939             var curve = this.addPoint(point);
41940             if (!lastPoint) {
41941                 this.drawDot({color: color, point: point});
41942             }
41943             else if (curve) {
41944                 this.drawCurve({color: color, curve: curve});
41945             }
41946             lastPoints.push({
41947                 time: point.time,
41948                 x: point.x,
41949                 y: point.y
41950             });
41951         }
41952     },
41953     
41954     strokeEnd: function(e)
41955     {
41956         this.strokeUpdate(e);
41957         if (typeof this.onEnd === 'function') {
41958             this.onEnd(e);
41959         }
41960     },
41961     
41962     addPoint:  function (point) {
41963         var _lastPoints = this._lastPoints;
41964         _lastPoints.push(point);
41965         if (_lastPoints.length > 2) {
41966             if (_lastPoints.length === 3) {
41967                 _lastPoints.unshift(_lastPoints[0]);
41968             }
41969             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
41970             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
41971             _lastPoints.shift();
41972             return curve;
41973         }
41974         return null;
41975     },
41976     
41977     calculateCurveWidths: function (startPoint, endPoint) {
41978         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
41979             (1 - this.velocity_filter_weight) * this._lastVelocity;
41980
41981         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
41982         var widths = {
41983             end: newWidth,
41984             start: this._lastWidth
41985         };
41986         
41987         this._lastVelocity = velocity;
41988         this._lastWidth = newWidth;
41989         return widths;
41990     },
41991     
41992     drawDot: function (_a) {
41993         var color = _a.color, point = _a.point;
41994         var ctx = this.canvasElCtx();
41995         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
41996         ctx.beginPath();
41997         this.drawCurveSegment(point.x, point.y, width);
41998         ctx.closePath();
41999         ctx.fillStyle = color;
42000         ctx.fill();
42001     },
42002     
42003     drawCurve: function (_a) {
42004         var color = _a.color, curve = _a.curve;
42005         var ctx = this.canvasElCtx();
42006         var widthDelta = curve.endWidth - curve.startWidth;
42007         var drawSteps = Math.floor(curve.length()) * 2;
42008         ctx.beginPath();
42009         ctx.fillStyle = color;
42010         for (var i = 0; i < drawSteps; i += 1) {
42011         var t = i / drawSteps;
42012         var tt = t * t;
42013         var ttt = tt * t;
42014         var u = 1 - t;
42015         var uu = u * u;
42016         var uuu = uu * u;
42017         var x = uuu * curve.startPoint.x;
42018         x += 3 * uu * t * curve.control1.x;
42019         x += 3 * u * tt * curve.control2.x;
42020         x += ttt * curve.endPoint.x;
42021         var y = uuu * curve.startPoint.y;
42022         y += 3 * uu * t * curve.control1.y;
42023         y += 3 * u * tt * curve.control2.y;
42024         y += ttt * curve.endPoint.y;
42025         var width = curve.startWidth + ttt * widthDelta;
42026         this.drawCurveSegment(x, y, width);
42027         }
42028         ctx.closePath();
42029         ctx.fill();
42030     },
42031     
42032     drawCurveSegment: function (x, y, width) {
42033         var ctx = this.canvasElCtx();
42034         ctx.moveTo(x, y);
42035         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42036         this.is_empty = false;
42037     },
42038     
42039     clear: function()
42040     {
42041         var ctx = this.canvasElCtx();
42042         var canvas = this.canvasEl().dom;
42043         ctx.fillStyle = this.bg_color;
42044         ctx.clearRect(0, 0, canvas.width, canvas.height);
42045         ctx.fillRect(0, 0, canvas.width, canvas.height);
42046         this.curve_data = [];
42047         this.reset();
42048         this.is_empty = true;
42049     },
42050     
42051     fileEl: function()
42052     {
42053         return  this.el.select('input',true).first();
42054     },
42055     
42056     canvasEl: function()
42057     {
42058         return this.el.select('canvas',true).first();
42059     },
42060     
42061     canvasElCtx: function()
42062     {
42063         return this.el.select('canvas',true).first().dom.getContext('2d');
42064     },
42065     
42066     getImage: function(type)
42067     {
42068         if(this.is_empty) {
42069             return false;
42070         }
42071         
42072         // encryption ?
42073         return this.canvasEl().dom.toDataURL('image/'+type, 1);
42074     },
42075     
42076     drawFromImage: function(img_src)
42077     {
42078         var img = new Image();
42079         
42080         img.onload = function(){
42081             this.canvasElCtx().drawImage(img, 0, 0);
42082         }.bind(this);
42083         
42084         img.src = img_src;
42085         
42086         this.is_empty = false;
42087     },
42088     
42089     selectImage: function()
42090     {
42091         this.fileEl().dom.click();
42092     },
42093     
42094     uploadImage: function(e)
42095     {
42096         var reader = new FileReader();
42097         
42098         reader.onload = function(e){
42099             var img = new Image();
42100             img.onload = function(){
42101                 this.reset();
42102                 this.canvasElCtx().drawImage(img, 0, 0);
42103             }.bind(this);
42104             img.src = e.target.result;
42105         }.bind(this);
42106         
42107         reader.readAsDataURL(e.target.files[0]);
42108     },
42109     
42110     // Bezier Point Constructor
42111     Point: (function () {
42112         function Point(x, y, time) {
42113             this.x = x;
42114             this.y = y;
42115             this.time = time || Date.now();
42116         }
42117         Point.prototype.distanceTo = function (start) {
42118             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42119         };
42120         Point.prototype.equals = function (other) {
42121             return this.x === other.x && this.y === other.y && this.time === other.time;
42122         };
42123         Point.prototype.velocityFrom = function (start) {
42124             return this.time !== start.time
42125             ? this.distanceTo(start) / (this.time - start.time)
42126             : 0;
42127         };
42128         return Point;
42129     }()),
42130     
42131     
42132     // Bezier Constructor
42133     Bezier: (function () {
42134         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42135             this.startPoint = startPoint;
42136             this.control2 = control2;
42137             this.control1 = control1;
42138             this.endPoint = endPoint;
42139             this.startWidth = startWidth;
42140             this.endWidth = endWidth;
42141         }
42142         Bezier.fromPoints = function (points, widths, scope) {
42143             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42144             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42145             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42146         };
42147         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42148             var dx1 = s1.x - s2.x;
42149             var dy1 = s1.y - s2.y;
42150             var dx2 = s2.x - s3.x;
42151             var dy2 = s2.y - s3.y;
42152             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42153             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42154             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42155             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42156             var dxm = m1.x - m2.x;
42157             var dym = m1.y - m2.y;
42158             var k = l2 / (l1 + l2);
42159             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42160             var tx = s2.x - cm.x;
42161             var ty = s2.y - cm.y;
42162             return {
42163                 c1: new scope.Point(m1.x + tx, m1.y + ty),
42164                 c2: new scope.Point(m2.x + tx, m2.y + ty)
42165             };
42166         };
42167         Bezier.prototype.length = function () {
42168             var steps = 10;
42169             var length = 0;
42170             var px;
42171             var py;
42172             for (var i = 0; i <= steps; i += 1) {
42173                 var t = i / steps;
42174                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42175                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42176                 if (i > 0) {
42177                     var xdiff = cx - px;
42178                     var ydiff = cy - py;
42179                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42180                 }
42181                 px = cx;
42182                 py = cy;
42183             }
42184             return length;
42185         };
42186         Bezier.prototype.point = function (t, start, c1, c2, end) {
42187             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42188             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42189             + (3.0 * c2 * (1.0 - t) * t * t)
42190             + (end * t * t * t);
42191         };
42192         return Bezier;
42193     }()),
42194     
42195     throttleStroke: function(fn, wait) {
42196       if (wait === void 0) { wait = 250; }
42197       var previous = 0;
42198       var timeout = null;
42199       var result;
42200       var storedContext;
42201       var storedArgs;
42202       var later = function () {
42203           previous = Date.now();
42204           timeout = null;
42205           result = fn.apply(storedContext, storedArgs);
42206           if (!timeout) {
42207               storedContext = null;
42208               storedArgs = [];
42209           }
42210       };
42211       return function wrapper() {
42212           var args = [];
42213           for (var _i = 0; _i < arguments.length; _i++) {
42214               args[_i] = arguments[_i];
42215           }
42216           var now = Date.now();
42217           var remaining = wait - (now - previous);
42218           storedContext = this;
42219           storedArgs = args;
42220           if (remaining <= 0 || remaining > wait) {
42221               if (timeout) {
42222                   clearTimeout(timeout);
42223                   timeout = null;
42224               }
42225               previous = now;
42226               result = fn.apply(storedContext, storedArgs);
42227               if (!timeout) {
42228                   storedContext = null;
42229                   storedArgs = [];
42230               }
42231           }
42232           else if (!timeout) {
42233               timeout = window.setTimeout(later, remaining);
42234           }
42235           return result;
42236       };
42237   }
42238   
42239 });
42240
42241  
42242
42243