a517377b5f181cca11dbc9c945934539070a06c0
[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      * Create an empty record
12320      * @param {Object} data (optional) - overlay some values
12321      * @return {Roo.data.Record} record created.
12322      */
12323     newRow :  function(d) {
12324         var da =  {};
12325         this.recordType.prototype.fields.each(function(c) {
12326             switch( c.type) {
12327                 case 'int' : da[c.name] = 0; break;
12328                 case 'date' : da[c.name] = new Date(); break;
12329                 case 'float' : da[c.name] = 0.0; break;
12330                 case 'boolean' : da[c.name] = false; break;
12331                 default : da[c.name] = ""; break;
12332             }
12333             
12334         });
12335         return new this.recordType(Roo.apply(da, d));
12336     }
12337     
12338 };/*
12339  * Based on:
12340  * Ext JS Library 1.1.1
12341  * Copyright(c) 2006-2007, Ext JS, LLC.
12342  *
12343  * Originally Released Under LGPL - original licence link has changed is not relivant.
12344  *
12345  * Fork - LGPL
12346  * <script type="text/javascript">
12347  */
12348
12349 /**
12350  * @class Roo.data.DataProxy
12351  * @extends Roo.data.Observable
12352  * This class is an abstract base class for implementations which provide retrieval of
12353  * unformatted data objects.<br>
12354  * <p>
12355  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12356  * (of the appropriate type which knows how to parse the data object) to provide a block of
12357  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12358  * <p>
12359  * Custom implementations must implement the load method as described in
12360  * {@link Roo.data.HttpProxy#load}.
12361  */
12362 Roo.data.DataProxy = function(){
12363     this.addEvents({
12364         /**
12365          * @event beforeload
12366          * Fires before a network request is made to retrieve a data object.
12367          * @param {Object} This DataProxy object.
12368          * @param {Object} params The params parameter to the load function.
12369          */
12370         beforeload : true,
12371         /**
12372          * @event load
12373          * Fires before the load method's callback is called.
12374          * @param {Object} This DataProxy object.
12375          * @param {Object} o The data object.
12376          * @param {Object} arg The callback argument object passed to the load function.
12377          */
12378         load : true,
12379         /**
12380          * @event loadexception
12381          * Fires if an Exception occurs during data retrieval.
12382          * @param {Object} This DataProxy object.
12383          * @param {Object} o The data object.
12384          * @param {Object} arg The callback argument object passed to the load function.
12385          * @param {Object} e The Exception.
12386          */
12387         loadexception : true
12388     });
12389     Roo.data.DataProxy.superclass.constructor.call(this);
12390 };
12391
12392 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12393
12394     /**
12395      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12396      */
12397 /*
12398  * Based on:
12399  * Ext JS Library 1.1.1
12400  * Copyright(c) 2006-2007, Ext JS, LLC.
12401  *
12402  * Originally Released Under LGPL - original licence link has changed is not relivant.
12403  *
12404  * Fork - LGPL
12405  * <script type="text/javascript">
12406  */
12407 /**
12408  * @class Roo.data.MemoryProxy
12409  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12410  * to the Reader when its load method is called.
12411  * @constructor
12412  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12413  */
12414 Roo.data.MemoryProxy = function(data){
12415     if (data.data) {
12416         data = data.data;
12417     }
12418     Roo.data.MemoryProxy.superclass.constructor.call(this);
12419     this.data = data;
12420 };
12421
12422 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12423     
12424     /**
12425      * Load data from the requested source (in this case an in-memory
12426      * data object passed to the constructor), read the data object into
12427      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12428      * process that block using the passed callback.
12429      * @param {Object} params This parameter is not used by the MemoryProxy class.
12430      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12431      * object into a block of Roo.data.Records.
12432      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12433      * The function must be passed <ul>
12434      * <li>The Record block object</li>
12435      * <li>The "arg" argument from the load function</li>
12436      * <li>A boolean success indicator</li>
12437      * </ul>
12438      * @param {Object} scope The scope in which to call the callback
12439      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12440      */
12441     load : function(params, reader, callback, scope, arg){
12442         params = params || {};
12443         var result;
12444         try {
12445             result = reader.readRecords(params.data ? params.data :this.data);
12446         }catch(e){
12447             this.fireEvent("loadexception", this, arg, null, e);
12448             callback.call(scope, null, arg, false);
12449             return;
12450         }
12451         callback.call(scope, result, arg, true);
12452     },
12453     
12454     // private
12455     update : function(params, records){
12456         
12457     }
12458 });/*
12459  * Based on:
12460  * Ext JS Library 1.1.1
12461  * Copyright(c) 2006-2007, Ext JS, LLC.
12462  *
12463  * Originally Released Under LGPL - original licence link has changed is not relivant.
12464  *
12465  * Fork - LGPL
12466  * <script type="text/javascript">
12467  */
12468 /**
12469  * @class Roo.data.HttpProxy
12470  * @extends Roo.data.DataProxy
12471  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12472  * configured to reference a certain URL.<br><br>
12473  * <p>
12474  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12475  * from which the running page was served.<br><br>
12476  * <p>
12477  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12478  * <p>
12479  * Be aware that to enable the browser to parse an XML document, the server must set
12480  * the Content-Type header in the HTTP response to "text/xml".
12481  * @constructor
12482  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12483  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12484  * will be used to make the request.
12485  */
12486 Roo.data.HttpProxy = function(conn){
12487     Roo.data.HttpProxy.superclass.constructor.call(this);
12488     // is conn a conn config or a real conn?
12489     this.conn = conn;
12490     this.useAjax = !conn || !conn.events;
12491   
12492 };
12493
12494 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12495     // thse are take from connection...
12496     
12497     /**
12498      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12499      */
12500     /**
12501      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12502      * extra parameters to each request made by this object. (defaults to undefined)
12503      */
12504     /**
12505      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12506      *  to each request made by this object. (defaults to undefined)
12507      */
12508     /**
12509      * @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)
12510      */
12511     /**
12512      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12513      */
12514      /**
12515      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12516      * @type Boolean
12517      */
12518   
12519
12520     /**
12521      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12522      * @type Boolean
12523      */
12524     /**
12525      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12526      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12527      * a finer-grained basis than the DataProxy events.
12528      */
12529     getConnection : function(){
12530         return this.useAjax ? Roo.Ajax : this.conn;
12531     },
12532
12533     /**
12534      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12535      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12536      * process that block using the passed callback.
12537      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12538      * for the request to the remote server.
12539      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12540      * object into a block of Roo.data.Records.
12541      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12542      * The function must be passed <ul>
12543      * <li>The Record block object</li>
12544      * <li>The "arg" argument from the load function</li>
12545      * <li>A boolean success indicator</li>
12546      * </ul>
12547      * @param {Object} scope The scope in which to call the callback
12548      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12549      */
12550     load : function(params, reader, callback, scope, arg){
12551         if(this.fireEvent("beforeload", this, params) !== false){
12552             var  o = {
12553                 params : params || {},
12554                 request: {
12555                     callback : callback,
12556                     scope : scope,
12557                     arg : arg
12558                 },
12559                 reader: reader,
12560                 callback : this.loadResponse,
12561                 scope: this
12562             };
12563             if(this.useAjax){
12564                 Roo.applyIf(o, this.conn);
12565                 if(this.activeRequest){
12566                     Roo.Ajax.abort(this.activeRequest);
12567                 }
12568                 this.activeRequest = Roo.Ajax.request(o);
12569             }else{
12570                 this.conn.request(o);
12571             }
12572         }else{
12573             callback.call(scope||this, null, arg, false);
12574         }
12575     },
12576
12577     // private
12578     loadResponse : function(o, success, response){
12579         delete this.activeRequest;
12580         if(!success){
12581             this.fireEvent("loadexception", this, o, response);
12582             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12583             return;
12584         }
12585         var result;
12586         try {
12587             result = o.reader.read(response);
12588         }catch(e){
12589             this.fireEvent("loadexception", this, o, response, e);
12590             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12591             return;
12592         }
12593         
12594         this.fireEvent("load", this, o, o.request.arg);
12595         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12596     },
12597
12598     // private
12599     update : function(dataSet){
12600
12601     },
12602
12603     // private
12604     updateResponse : function(dataSet){
12605
12606     }
12607 });/*
12608  * Based on:
12609  * Ext JS Library 1.1.1
12610  * Copyright(c) 2006-2007, Ext JS, LLC.
12611  *
12612  * Originally Released Under LGPL - original licence link has changed is not relivant.
12613  *
12614  * Fork - LGPL
12615  * <script type="text/javascript">
12616  */
12617
12618 /**
12619  * @class Roo.data.ScriptTagProxy
12620  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12621  * other than the originating domain of the running page.<br><br>
12622  * <p>
12623  * <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
12624  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12625  * <p>
12626  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12627  * source code that is used as the source inside a &lt;script> tag.<br><br>
12628  * <p>
12629  * In order for the browser to process the returned data, the server must wrap the data object
12630  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12631  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12632  * depending on whether the callback name was passed:
12633  * <p>
12634  * <pre><code>
12635 boolean scriptTag = false;
12636 String cb = request.getParameter("callback");
12637 if (cb != null) {
12638     scriptTag = true;
12639     response.setContentType("text/javascript");
12640 } else {
12641     response.setContentType("application/x-json");
12642 }
12643 Writer out = response.getWriter();
12644 if (scriptTag) {
12645     out.write(cb + "(");
12646 }
12647 out.print(dataBlock.toJsonString());
12648 if (scriptTag) {
12649     out.write(");");
12650 }
12651 </pre></code>
12652  *
12653  * @constructor
12654  * @param {Object} config A configuration object.
12655  */
12656 Roo.data.ScriptTagProxy = function(config){
12657     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12658     Roo.apply(this, config);
12659     this.head = document.getElementsByTagName("head")[0];
12660 };
12661
12662 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12663
12664 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12665     /**
12666      * @cfg {String} url The URL from which to request the data object.
12667      */
12668     /**
12669      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12670      */
12671     timeout : 30000,
12672     /**
12673      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12674      * the server the name of the callback function set up by the load call to process the returned data object.
12675      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12676      * javascript output which calls this named function passing the data object as its only parameter.
12677      */
12678     callbackParam : "callback",
12679     /**
12680      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12681      * name to the request.
12682      */
12683     nocache : true,
12684
12685     /**
12686      * Load data from the configured URL, read the data object into
12687      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12688      * process that block using the passed callback.
12689      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12690      * for the request to the remote server.
12691      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12692      * object into a block of Roo.data.Records.
12693      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12694      * The function must be passed <ul>
12695      * <li>The Record block object</li>
12696      * <li>The "arg" argument from the load function</li>
12697      * <li>A boolean success indicator</li>
12698      * </ul>
12699      * @param {Object} scope The scope in which to call the callback
12700      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12701      */
12702     load : function(params, reader, callback, scope, arg){
12703         if(this.fireEvent("beforeload", this, params) !== false){
12704
12705             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12706
12707             var url = this.url;
12708             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12709             if(this.nocache){
12710                 url += "&_dc=" + (new Date().getTime());
12711             }
12712             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12713             var trans = {
12714                 id : transId,
12715                 cb : "stcCallback"+transId,
12716                 scriptId : "stcScript"+transId,
12717                 params : params,
12718                 arg : arg,
12719                 url : url,
12720                 callback : callback,
12721                 scope : scope,
12722                 reader : reader
12723             };
12724             var conn = this;
12725
12726             window[trans.cb] = function(o){
12727                 conn.handleResponse(o, trans);
12728             };
12729
12730             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12731
12732             if(this.autoAbort !== false){
12733                 this.abort();
12734             }
12735
12736             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12737
12738             var script = document.createElement("script");
12739             script.setAttribute("src", url);
12740             script.setAttribute("type", "text/javascript");
12741             script.setAttribute("id", trans.scriptId);
12742             this.head.appendChild(script);
12743
12744             this.trans = trans;
12745         }else{
12746             callback.call(scope||this, null, arg, false);
12747         }
12748     },
12749
12750     // private
12751     isLoading : function(){
12752         return this.trans ? true : false;
12753     },
12754
12755     /**
12756      * Abort the current server request.
12757      */
12758     abort : function(){
12759         if(this.isLoading()){
12760             this.destroyTrans(this.trans);
12761         }
12762     },
12763
12764     // private
12765     destroyTrans : function(trans, isLoaded){
12766         this.head.removeChild(document.getElementById(trans.scriptId));
12767         clearTimeout(trans.timeoutId);
12768         if(isLoaded){
12769             window[trans.cb] = undefined;
12770             try{
12771                 delete window[trans.cb];
12772             }catch(e){}
12773         }else{
12774             // if hasn't been loaded, wait for load to remove it to prevent script error
12775             window[trans.cb] = function(){
12776                 window[trans.cb] = undefined;
12777                 try{
12778                     delete window[trans.cb];
12779                 }catch(e){}
12780             };
12781         }
12782     },
12783
12784     // private
12785     handleResponse : function(o, trans){
12786         this.trans = false;
12787         this.destroyTrans(trans, true);
12788         var result;
12789         try {
12790             result = trans.reader.readRecords(o);
12791         }catch(e){
12792             this.fireEvent("loadexception", this, o, trans.arg, e);
12793             trans.callback.call(trans.scope||window, null, trans.arg, false);
12794             return;
12795         }
12796         this.fireEvent("load", this, o, trans.arg);
12797         trans.callback.call(trans.scope||window, result, trans.arg, true);
12798     },
12799
12800     // private
12801     handleFailure : function(trans){
12802         this.trans = false;
12803         this.destroyTrans(trans, false);
12804         this.fireEvent("loadexception", this, null, trans.arg);
12805         trans.callback.call(trans.scope||window, null, trans.arg, false);
12806     }
12807 });/*
12808  * Based on:
12809  * Ext JS Library 1.1.1
12810  * Copyright(c) 2006-2007, Ext JS, LLC.
12811  *
12812  * Originally Released Under LGPL - original licence link has changed is not relivant.
12813  *
12814  * Fork - LGPL
12815  * <script type="text/javascript">
12816  */
12817
12818 /**
12819  * @class Roo.data.JsonReader
12820  * @extends Roo.data.DataReader
12821  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12822  * based on mappings in a provided Roo.data.Record constructor.
12823  * 
12824  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12825  * in the reply previously. 
12826  * 
12827  * <p>
12828  * Example code:
12829  * <pre><code>
12830 var RecordDef = Roo.data.Record.create([
12831     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12832     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12833 ]);
12834 var myReader = new Roo.data.JsonReader({
12835     totalProperty: "results",    // The property which contains the total dataset size (optional)
12836     root: "rows",                // The property which contains an Array of row objects
12837     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12838 }, RecordDef);
12839 </code></pre>
12840  * <p>
12841  * This would consume a JSON file like this:
12842  * <pre><code>
12843 { 'results': 2, 'rows': [
12844     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12845     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12846 }
12847 </code></pre>
12848  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12849  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12850  * paged from the remote server.
12851  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12852  * @cfg {String} root name of the property which contains the Array of row objects.
12853  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12854  * @cfg {Array} fields Array of field definition objects
12855  * @constructor
12856  * Create a new JsonReader
12857  * @param {Object} meta Metadata configuration options
12858  * @param {Object} recordType Either an Array of field definition objects,
12859  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12860  */
12861 Roo.data.JsonReader = function(meta, recordType){
12862     
12863     meta = meta || {};
12864     // set some defaults:
12865     Roo.applyIf(meta, {
12866         totalProperty: 'total',
12867         successProperty : 'success',
12868         root : 'data',
12869         id : 'id'
12870     });
12871     
12872     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12873 };
12874 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12875     
12876     /**
12877      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12878      * Used by Store query builder to append _requestMeta to params.
12879      * 
12880      */
12881     metaFromRemote : false,
12882     /**
12883      * This method is only used by a DataProxy which has retrieved data from a remote server.
12884      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12885      * @return {Object} data A data block which is used by an Roo.data.Store object as
12886      * a cache of Roo.data.Records.
12887      */
12888     read : function(response){
12889         var json = response.responseText;
12890        
12891         var o = /* eval:var:o */ eval("("+json+")");
12892         if(!o) {
12893             throw {message: "JsonReader.read: Json object not found"};
12894         }
12895         
12896         if(o.metaData){
12897             
12898             delete this.ef;
12899             this.metaFromRemote = true;
12900             this.meta = o.metaData;
12901             this.recordType = Roo.data.Record.create(o.metaData.fields);
12902             this.onMetaChange(this.meta, this.recordType, o);
12903         }
12904         return this.readRecords(o);
12905     },
12906
12907     // private function a store will implement
12908     onMetaChange : function(meta, recordType, o){
12909
12910     },
12911
12912     /**
12913          * @ignore
12914          */
12915     simpleAccess: function(obj, subsc) {
12916         return obj[subsc];
12917     },
12918
12919         /**
12920          * @ignore
12921          */
12922     getJsonAccessor: function(){
12923         var re = /[\[\.]/;
12924         return function(expr) {
12925             try {
12926                 return(re.test(expr))
12927                     ? new Function("obj", "return obj." + expr)
12928                     : function(obj){
12929                         return obj[expr];
12930                     };
12931             } catch(e){}
12932             return Roo.emptyFn;
12933         };
12934     }(),
12935
12936     /**
12937      * Create a data block containing Roo.data.Records from an XML document.
12938      * @param {Object} o An object which contains an Array of row objects in the property specified
12939      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12940      * which contains the total size of the dataset.
12941      * @return {Object} data A data block which is used by an Roo.data.Store object as
12942      * a cache of Roo.data.Records.
12943      */
12944     readRecords : function(o){
12945         /**
12946          * After any data loads, the raw JSON data is available for further custom processing.
12947          * @type Object
12948          */
12949         this.o = o;
12950         var s = this.meta, Record = this.recordType,
12951             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12952
12953 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12954         if (!this.ef) {
12955             if(s.totalProperty) {
12956                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12957                 }
12958                 if(s.successProperty) {
12959                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12960                 }
12961                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12962                 if (s.id) {
12963                         var g = this.getJsonAccessor(s.id);
12964                         this.getId = function(rec) {
12965                                 var r = g(rec);  
12966                                 return (r === undefined || r === "") ? null : r;
12967                         };
12968                 } else {
12969                         this.getId = function(){return null;};
12970                 }
12971             this.ef = [];
12972             for(var jj = 0; jj < fl; jj++){
12973                 f = fi[jj];
12974                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12975                 this.ef[jj] = this.getJsonAccessor(map);
12976             }
12977         }
12978
12979         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12980         if(s.totalProperty){
12981             var vt = parseInt(this.getTotal(o), 10);
12982             if(!isNaN(vt)){
12983                 totalRecords = vt;
12984             }
12985         }
12986         if(s.successProperty){
12987             var vs = this.getSuccess(o);
12988             if(vs === false || vs === 'false'){
12989                 success = false;
12990             }
12991         }
12992         var records = [];
12993         for(var i = 0; i < c; i++){
12994                 var n = root[i];
12995             var values = {};
12996             var id = this.getId(n);
12997             for(var j = 0; j < fl; j++){
12998                 f = fi[j];
12999             var v = this.ef[j](n);
13000             if (!f.convert) {
13001                 Roo.log('missing convert for ' + f.name);
13002                 Roo.log(f);
13003                 continue;
13004             }
13005             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
13006             }
13007             var record = new Record(values, id);
13008             record.json = n;
13009             records[i] = record;
13010         }
13011         return {
13012             raw : o,
13013             success : success,
13014             records : records,
13015             totalRecords : totalRecords
13016         };
13017     }
13018 });/*
13019  * Based on:
13020  * Ext JS Library 1.1.1
13021  * Copyright(c) 2006-2007, Ext JS, LLC.
13022  *
13023  * Originally Released Under LGPL - original licence link has changed is not relivant.
13024  *
13025  * Fork - LGPL
13026  * <script type="text/javascript">
13027  */
13028
13029 /**
13030  * @class Roo.data.ArrayReader
13031  * @extends Roo.data.DataReader
13032  * Data reader class to create an Array of Roo.data.Record objects from an Array.
13033  * Each element of that Array represents a row of data fields. The
13034  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13035  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13036  * <p>
13037  * Example code:.
13038  * <pre><code>
13039 var RecordDef = Roo.data.Record.create([
13040     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
13041     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
13042 ]);
13043 var myReader = new Roo.data.ArrayReader({
13044     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
13045 }, RecordDef);
13046 </code></pre>
13047  * <p>
13048  * This would consume an Array like this:
13049  * <pre><code>
13050 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13051   </code></pre>
13052  
13053  * @constructor
13054  * Create a new JsonReader
13055  * @param {Object} meta Metadata configuration options.
13056  * @param {Object|Array} recordType Either an Array of field definition objects
13057  * 
13058  * @cfg {Array} fields Array of field definition objects
13059  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13060  * as specified to {@link Roo.data.Record#create},
13061  * or an {@link Roo.data.Record} object
13062  *
13063  * 
13064  * created using {@link Roo.data.Record#create}.
13065  */
13066 Roo.data.ArrayReader = function(meta, recordType){
13067     
13068      
13069     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13070 };
13071
13072 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13073     /**
13074      * Create a data block containing Roo.data.Records from an XML document.
13075      * @param {Object} o An Array of row objects which represents the dataset.
13076      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13077      * a cache of Roo.data.Records.
13078      */
13079     readRecords : function(o)
13080     {
13081         var sid = this.meta ? this.meta.id : null;
13082         var recordType = this.recordType, fields = recordType.prototype.fields;
13083         var records = [];
13084         var root = o;
13085         for(var i = 0; i < root.length; i++){
13086                 var n = root[i];
13087             var values = {};
13088             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13089             for(var j = 0, jlen = fields.length; j < jlen; j++){
13090                 var f = fields.items[j];
13091                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13092                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13093                 v = f.convert(v);
13094                 values[f.name] = v;
13095             }
13096             var record = new recordType(values, id);
13097             record.json = n;
13098             records[records.length] = record;
13099         }
13100         return {
13101             records : records,
13102             totalRecords : records.length
13103         };
13104     }
13105 });/*
13106  * - LGPL
13107  * * 
13108  */
13109
13110 /**
13111  * @class Roo.bootstrap.ComboBox
13112  * @extends Roo.bootstrap.TriggerField
13113  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13114  * @cfg {Boolean} append (true|false) default false
13115  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13116  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13117  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13118  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13119  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13120  * @cfg {Boolean} animate default true
13121  * @cfg {Boolean} emptyResultText only for touch device
13122  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13123  * @cfg {String} emptyTitle default ''
13124  * @constructor
13125  * Create a new ComboBox.
13126  * @param {Object} config Configuration options
13127  */
13128 Roo.bootstrap.ComboBox = function(config){
13129     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13130     this.addEvents({
13131         /**
13132          * @event expand
13133          * Fires when the dropdown list is expanded
13134         * @param {Roo.bootstrap.ComboBox} combo This combo box
13135         */
13136         'expand' : true,
13137         /**
13138          * @event collapse
13139          * Fires when the dropdown list is collapsed
13140         * @param {Roo.bootstrap.ComboBox} combo This combo box
13141         */
13142         'collapse' : true,
13143         /**
13144          * @event beforeselect
13145          * Fires before a list item is selected. Return false to cancel the selection.
13146         * @param {Roo.bootstrap.ComboBox} combo This combo box
13147         * @param {Roo.data.Record} record The data record returned from the underlying store
13148         * @param {Number} index The index of the selected item in the dropdown list
13149         */
13150         'beforeselect' : true,
13151         /**
13152          * @event select
13153          * Fires when a list item is selected
13154         * @param {Roo.bootstrap.ComboBox} combo This combo box
13155         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13156         * @param {Number} index The index of the selected item in the dropdown list
13157         */
13158         'select' : true,
13159         /**
13160          * @event beforequery
13161          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13162          * The event object passed has these properties:
13163         * @param {Roo.bootstrap.ComboBox} combo This combo box
13164         * @param {String} query The query
13165         * @param {Boolean} forceAll true to force "all" query
13166         * @param {Boolean} cancel true to cancel the query
13167         * @param {Object} e The query event object
13168         */
13169         'beforequery': true,
13170          /**
13171          * @event add
13172          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13173         * @param {Roo.bootstrap.ComboBox} combo This combo box
13174         */
13175         'add' : true,
13176         /**
13177          * @event edit
13178          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13179         * @param {Roo.bootstrap.ComboBox} combo This combo box
13180         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13181         */
13182         'edit' : true,
13183         /**
13184          * @event remove
13185          * Fires when the remove value from the combobox array
13186         * @param {Roo.bootstrap.ComboBox} combo This combo box
13187         */
13188         'remove' : true,
13189         /**
13190          * @event afterremove
13191          * Fires when the remove value from the combobox array
13192         * @param {Roo.bootstrap.ComboBox} combo This combo box
13193         */
13194         'afterremove' : true,
13195         /**
13196          * @event specialfilter
13197          * Fires when specialfilter
13198             * @param {Roo.bootstrap.ComboBox} combo This combo box
13199             */
13200         'specialfilter' : true,
13201         /**
13202          * @event tick
13203          * Fires when tick the element
13204             * @param {Roo.bootstrap.ComboBox} combo This combo box
13205             */
13206         'tick' : true,
13207         /**
13208          * @event touchviewdisplay
13209          * Fires when touch view require special display (default is using displayField)
13210             * @param {Roo.bootstrap.ComboBox} combo This combo box
13211             * @param {Object} cfg set html .
13212             */
13213         'touchviewdisplay' : true
13214         
13215     });
13216     
13217     this.item = [];
13218     this.tickItems = [];
13219     
13220     this.selectedIndex = -1;
13221     if(this.mode == 'local'){
13222         if(config.queryDelay === undefined){
13223             this.queryDelay = 10;
13224         }
13225         if(config.minChars === undefined){
13226             this.minChars = 0;
13227         }
13228     }
13229 };
13230
13231 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13232      
13233     /**
13234      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13235      * rendering into an Roo.Editor, defaults to false)
13236      */
13237     /**
13238      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13239      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13240      */
13241     /**
13242      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13243      */
13244     /**
13245      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13246      * the dropdown list (defaults to undefined, with no header element)
13247      */
13248
13249      /**
13250      * @cfg {String/Roo.Template} tpl The template to use to render the output
13251      */
13252      
13253      /**
13254      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13255      */
13256     listWidth: undefined,
13257     /**
13258      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13259      * mode = 'remote' or 'text' if mode = 'local')
13260      */
13261     displayField: undefined,
13262     
13263     /**
13264      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13265      * mode = 'remote' or 'value' if mode = 'local'). 
13266      * Note: use of a valueField requires the user make a selection
13267      * in order for a value to be mapped.
13268      */
13269     valueField: undefined,
13270     /**
13271      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13272      */
13273     modalTitle : '',
13274     
13275     /**
13276      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13277      * field's data value (defaults to the underlying DOM element's name)
13278      */
13279     hiddenName: undefined,
13280     /**
13281      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13282      */
13283     listClass: '',
13284     /**
13285      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13286      */
13287     selectedClass: 'active',
13288     
13289     /**
13290      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13291      */
13292     shadow:'sides',
13293     /**
13294      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13295      * anchor positions (defaults to 'tl-bl')
13296      */
13297     listAlign: 'tl-bl?',
13298     /**
13299      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13300      */
13301     maxHeight: 300,
13302     /**
13303      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13304      * query specified by the allQuery config option (defaults to 'query')
13305      */
13306     triggerAction: 'query',
13307     /**
13308      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13309      * (defaults to 4, does not apply if editable = false)
13310      */
13311     minChars : 4,
13312     /**
13313      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13314      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13315      */
13316     typeAhead: false,
13317     /**
13318      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13319      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13320      */
13321     queryDelay: 500,
13322     /**
13323      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13324      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13325      */
13326     pageSize: 0,
13327     /**
13328      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13329      * when editable = true (defaults to false)
13330      */
13331     selectOnFocus:false,
13332     /**
13333      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13334      */
13335     queryParam: 'query',
13336     /**
13337      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13338      * when mode = 'remote' (defaults to 'Loading...')
13339      */
13340     loadingText: 'Loading...',
13341     /**
13342      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13343      */
13344     resizable: false,
13345     /**
13346      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13347      */
13348     handleHeight : 8,
13349     /**
13350      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13351      * traditional select (defaults to true)
13352      */
13353     editable: true,
13354     /**
13355      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13356      */
13357     allQuery: '',
13358     /**
13359      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13360      */
13361     mode: 'remote',
13362     /**
13363      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13364      * listWidth has a higher value)
13365      */
13366     minListWidth : 70,
13367     /**
13368      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13369      * allow the user to set arbitrary text into the field (defaults to false)
13370      */
13371     forceSelection:false,
13372     /**
13373      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13374      * if typeAhead = true (defaults to 250)
13375      */
13376     typeAheadDelay : 250,
13377     /**
13378      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13379      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13380      */
13381     valueNotFoundText : undefined,
13382     /**
13383      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13384      */
13385     blockFocus : false,
13386     
13387     /**
13388      * @cfg {Boolean} disableClear Disable showing of clear button.
13389      */
13390     disableClear : false,
13391     /**
13392      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13393      */
13394     alwaysQuery : false,
13395     
13396     /**
13397      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13398      */
13399     multiple : false,
13400     
13401     /**
13402      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13403      */
13404     invalidClass : "has-warning",
13405     
13406     /**
13407      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13408      */
13409     validClass : "has-success",
13410     
13411     /**
13412      * @cfg {Boolean} specialFilter (true|false) special filter default false
13413      */
13414     specialFilter : false,
13415     
13416     /**
13417      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13418      */
13419     mobileTouchView : true,
13420     
13421     /**
13422      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13423      */
13424     useNativeIOS : false,
13425     
13426     /**
13427      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13428      */
13429     mobile_restrict_height : false,
13430     
13431     ios_options : false,
13432     
13433     //private
13434     addicon : false,
13435     editicon: false,
13436     
13437     page: 0,
13438     hasQuery: false,
13439     append: false,
13440     loadNext: false,
13441     autoFocus : true,
13442     tickable : false,
13443     btnPosition : 'right',
13444     triggerList : true,
13445     showToggleBtn : true,
13446     animate : true,
13447     emptyResultText: 'Empty',
13448     triggerText : 'Select',
13449     emptyTitle : '',
13450     
13451     // element that contains real text value.. (when hidden is used..)
13452     
13453     getAutoCreate : function()
13454     {   
13455         var cfg = false;
13456         //render
13457         /*
13458          * Render classic select for iso
13459          */
13460         
13461         if(Roo.isIOS && this.useNativeIOS){
13462             cfg = this.getAutoCreateNativeIOS();
13463             return cfg;
13464         }
13465         
13466         /*
13467          * Touch Devices
13468          */
13469         
13470         if(Roo.isTouch && this.mobileTouchView){
13471             cfg = this.getAutoCreateTouchView();
13472             return cfg;;
13473         }
13474         
13475         /*
13476          *  Normal ComboBox
13477          */
13478         if(!this.tickable){
13479             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13480             return cfg;
13481         }
13482         
13483         /*
13484          *  ComboBox with tickable selections
13485          */
13486              
13487         var align = this.labelAlign || this.parentLabelAlign();
13488         
13489         cfg = {
13490             cls : 'form-group roo-combobox-tickable' //input-group
13491         };
13492         
13493         var btn_text_select = '';
13494         var btn_text_done = '';
13495         var btn_text_cancel = '';
13496         
13497         if (this.btn_text_show) {
13498             btn_text_select = 'Select';
13499             btn_text_done = 'Done';
13500             btn_text_cancel = 'Cancel'; 
13501         }
13502         
13503         var buttons = {
13504             tag : 'div',
13505             cls : 'tickable-buttons',
13506             cn : [
13507                 {
13508                     tag : 'button',
13509                     type : 'button',
13510                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13511                     //html : this.triggerText
13512                     html: btn_text_select
13513                 },
13514                 {
13515                     tag : 'button',
13516                     type : 'button',
13517                     name : 'ok',
13518                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13519                     //html : 'Done'
13520                     html: btn_text_done
13521                 },
13522                 {
13523                     tag : 'button',
13524                     type : 'button',
13525                     name : 'cancel',
13526                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13527                     //html : 'Cancel'
13528                     html: btn_text_cancel
13529                 }
13530             ]
13531         };
13532         
13533         if(this.editable){
13534             buttons.cn.unshift({
13535                 tag: 'input',
13536                 cls: 'roo-select2-search-field-input'
13537             });
13538         }
13539         
13540         var _this = this;
13541         
13542         Roo.each(buttons.cn, function(c){
13543             if (_this.size) {
13544                 c.cls += ' btn-' + _this.size;
13545             }
13546
13547             if (_this.disabled) {
13548                 c.disabled = true;
13549             }
13550         });
13551         
13552         var box = {
13553             tag: 'div',
13554             style : 'display: contents',
13555             cn: [
13556                 {
13557                     tag: 'input',
13558                     type : 'hidden',
13559                     cls: 'form-hidden-field'
13560                 },
13561                 {
13562                     tag: 'ul',
13563                     cls: 'roo-select2-choices',
13564                     cn:[
13565                         {
13566                             tag: 'li',
13567                             cls: 'roo-select2-search-field',
13568                             cn: [
13569                                 buttons
13570                             ]
13571                         }
13572                     ]
13573                 }
13574             ]
13575         };
13576         
13577         var combobox = {
13578             cls: 'roo-select2-container input-group roo-select2-container-multi',
13579             cn: [
13580                 
13581                 box
13582 //                {
13583 //                    tag: 'ul',
13584 //                    cls: 'typeahead typeahead-long dropdown-menu',
13585 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13586 //                }
13587             ]
13588         };
13589         
13590         if(this.hasFeedback && !this.allowBlank){
13591             
13592             var feedback = {
13593                 tag: 'span',
13594                 cls: 'glyphicon form-control-feedback'
13595             };
13596
13597             combobox.cn.push(feedback);
13598         }
13599         
13600         var indicator = {
13601             tag : 'i',
13602             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13603             tooltip : 'This field is required'
13604         };
13605         if (Roo.bootstrap.version == 4) {
13606             indicator = {
13607                 tag : 'i',
13608                 style : 'display:none'
13609             };
13610         }
13611         if (align ==='left' && this.fieldLabel.length) {
13612             
13613             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13614             
13615             cfg.cn = [
13616                 indicator,
13617                 {
13618                     tag: 'label',
13619                     'for' :  id,
13620                     cls : 'control-label col-form-label',
13621                     html : this.fieldLabel
13622
13623                 },
13624                 {
13625                     cls : "", 
13626                     cn: [
13627                         combobox
13628                     ]
13629                 }
13630
13631             ];
13632             
13633             var labelCfg = cfg.cn[1];
13634             var contentCfg = cfg.cn[2];
13635             
13636
13637             if(this.indicatorpos == 'right'){
13638                 
13639                 cfg.cn = [
13640                     {
13641                         tag: 'label',
13642                         'for' :  id,
13643                         cls : 'control-label col-form-label',
13644                         cn : [
13645                             {
13646                                 tag : 'span',
13647                                 html : this.fieldLabel
13648                             },
13649                             indicator
13650                         ]
13651                     },
13652                     {
13653                         cls : "",
13654                         cn: [
13655                             combobox
13656                         ]
13657                     }
13658
13659                 ];
13660                 
13661                 
13662                 
13663                 labelCfg = cfg.cn[0];
13664                 contentCfg = cfg.cn[1];
13665             
13666             }
13667             
13668             if(this.labelWidth > 12){
13669                 labelCfg.style = "width: " + this.labelWidth + 'px';
13670             }
13671             
13672             if(this.labelWidth < 13 && this.labelmd == 0){
13673                 this.labelmd = this.labelWidth;
13674             }
13675             
13676             if(this.labellg > 0){
13677                 labelCfg.cls += ' col-lg-' + this.labellg;
13678                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13679             }
13680             
13681             if(this.labelmd > 0){
13682                 labelCfg.cls += ' col-md-' + this.labelmd;
13683                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13684             }
13685             
13686             if(this.labelsm > 0){
13687                 labelCfg.cls += ' col-sm-' + this.labelsm;
13688                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13689             }
13690             
13691             if(this.labelxs > 0){
13692                 labelCfg.cls += ' col-xs-' + this.labelxs;
13693                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13694             }
13695                 
13696                 
13697         } else if ( this.fieldLabel.length) {
13698 //                Roo.log(" label");
13699                  cfg.cn = [
13700                    indicator,
13701                     {
13702                         tag: 'label',
13703                         //cls : 'input-group-addon',
13704                         html : this.fieldLabel
13705                     },
13706                     combobox
13707                 ];
13708                 
13709                 if(this.indicatorpos == 'right'){
13710                     cfg.cn = [
13711                         {
13712                             tag: 'label',
13713                             //cls : 'input-group-addon',
13714                             html : this.fieldLabel
13715                         },
13716                         indicator,
13717                         combobox
13718                     ];
13719                     
13720                 }
13721
13722         } else {
13723             
13724 //                Roo.log(" no label && no align");
13725                 cfg = combobox
13726                      
13727                 
13728         }
13729          
13730         var settings=this;
13731         ['xs','sm','md','lg'].map(function(size){
13732             if (settings[size]) {
13733                 cfg.cls += ' col-' + size + '-' + settings[size];
13734             }
13735         });
13736         
13737         return cfg;
13738         
13739     },
13740     
13741     _initEventsCalled : false,
13742     
13743     // private
13744     initEvents: function()
13745     {   
13746         if (this._initEventsCalled) { // as we call render... prevent looping...
13747             return;
13748         }
13749         this._initEventsCalled = true;
13750         
13751         if (!this.store) {
13752             throw "can not find store for combo";
13753         }
13754         
13755         this.indicator = this.indicatorEl();
13756         
13757         this.store = Roo.factory(this.store, Roo.data);
13758         this.store.parent = this;
13759         
13760         // if we are building from html. then this element is so complex, that we can not really
13761         // use the rendered HTML.
13762         // so we have to trash and replace the previous code.
13763         if (Roo.XComponent.build_from_html) {
13764             // remove this element....
13765             var e = this.el.dom, k=0;
13766             while (e ) { e = e.previousSibling;  ++k;}
13767
13768             this.el.remove();
13769             
13770             this.el=false;
13771             this.rendered = false;
13772             
13773             this.render(this.parent().getChildContainer(true), k);
13774         }
13775         
13776         if(Roo.isIOS && this.useNativeIOS){
13777             this.initIOSView();
13778             return;
13779         }
13780         
13781         /*
13782          * Touch Devices
13783          */
13784         
13785         if(Roo.isTouch && this.mobileTouchView){
13786             this.initTouchView();
13787             return;
13788         }
13789         
13790         if(this.tickable){
13791             this.initTickableEvents();
13792             return;
13793         }
13794         
13795         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13796         
13797         if(this.hiddenName){
13798             
13799             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13800             
13801             this.hiddenField.dom.value =
13802                 this.hiddenValue !== undefined ? this.hiddenValue :
13803                 this.value !== undefined ? this.value : '';
13804
13805             // prevent input submission
13806             this.el.dom.removeAttribute('name');
13807             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13808              
13809              
13810         }
13811         //if(Roo.isGecko){
13812         //    this.el.dom.setAttribute('autocomplete', 'off');
13813         //}
13814         
13815         var cls = 'x-combo-list';
13816         
13817         //this.list = new Roo.Layer({
13818         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13819         //});
13820         
13821         var _this = this;
13822         
13823         (function(){
13824             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13825             _this.list.setWidth(lw);
13826         }).defer(100);
13827         
13828         this.list.on('mouseover', this.onViewOver, this);
13829         this.list.on('mousemove', this.onViewMove, this);
13830         this.list.on('scroll', this.onViewScroll, this);
13831         
13832         /*
13833         this.list.swallowEvent('mousewheel');
13834         this.assetHeight = 0;
13835
13836         if(this.title){
13837             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13838             this.assetHeight += this.header.getHeight();
13839         }
13840
13841         this.innerList = this.list.createChild({cls:cls+'-inner'});
13842         this.innerList.on('mouseover', this.onViewOver, this);
13843         this.innerList.on('mousemove', this.onViewMove, this);
13844         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13845         
13846         if(this.allowBlank && !this.pageSize && !this.disableClear){
13847             this.footer = this.list.createChild({cls:cls+'-ft'});
13848             this.pageTb = new Roo.Toolbar(this.footer);
13849            
13850         }
13851         if(this.pageSize){
13852             this.footer = this.list.createChild({cls:cls+'-ft'});
13853             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13854                     {pageSize: this.pageSize});
13855             
13856         }
13857         
13858         if (this.pageTb && this.allowBlank && !this.disableClear) {
13859             var _this = this;
13860             this.pageTb.add(new Roo.Toolbar.Fill(), {
13861                 cls: 'x-btn-icon x-btn-clear',
13862                 text: '&#160;',
13863                 handler: function()
13864                 {
13865                     _this.collapse();
13866                     _this.clearValue();
13867                     _this.onSelect(false, -1);
13868                 }
13869             });
13870         }
13871         if (this.footer) {
13872             this.assetHeight += this.footer.getHeight();
13873         }
13874         */
13875             
13876         if(!this.tpl){
13877             this.tpl = Roo.bootstrap.version == 4 ?
13878                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13879                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13880         }
13881
13882         this.view = new Roo.View(this.list, this.tpl, {
13883             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13884         });
13885         //this.view.wrapEl.setDisplayed(false);
13886         this.view.on('click', this.onViewClick, this);
13887         
13888         
13889         this.store.on('beforeload', this.onBeforeLoad, this);
13890         this.store.on('load', this.onLoad, this);
13891         this.store.on('loadexception', this.onLoadException, this);
13892         /*
13893         if(this.resizable){
13894             this.resizer = new Roo.Resizable(this.list,  {
13895                pinned:true, handles:'se'
13896             });
13897             this.resizer.on('resize', function(r, w, h){
13898                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13899                 this.listWidth = w;
13900                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13901                 this.restrictHeight();
13902             }, this);
13903             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13904         }
13905         */
13906         if(!this.editable){
13907             this.editable = true;
13908             this.setEditable(false);
13909         }
13910         
13911         /*
13912         
13913         if (typeof(this.events.add.listeners) != 'undefined') {
13914             
13915             this.addicon = this.wrap.createChild(
13916                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13917        
13918             this.addicon.on('click', function(e) {
13919                 this.fireEvent('add', this);
13920             }, this);
13921         }
13922         if (typeof(this.events.edit.listeners) != 'undefined') {
13923             
13924             this.editicon = this.wrap.createChild(
13925                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13926             if (this.addicon) {
13927                 this.editicon.setStyle('margin-left', '40px');
13928             }
13929             this.editicon.on('click', function(e) {
13930                 
13931                 // we fire even  if inothing is selected..
13932                 this.fireEvent('edit', this, this.lastData );
13933                 
13934             }, this);
13935         }
13936         */
13937         
13938         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13939             "up" : function(e){
13940                 this.inKeyMode = true;
13941                 this.selectPrev();
13942             },
13943
13944             "down" : function(e){
13945                 if(!this.isExpanded()){
13946                     this.onTriggerClick();
13947                 }else{
13948                     this.inKeyMode = true;
13949                     this.selectNext();
13950                 }
13951             },
13952
13953             "enter" : function(e){
13954 //                this.onViewClick();
13955                 //return true;
13956                 this.collapse();
13957                 
13958                 if(this.fireEvent("specialkey", this, e)){
13959                     this.onViewClick(false);
13960                 }
13961                 
13962                 return true;
13963             },
13964
13965             "esc" : function(e){
13966                 this.collapse();
13967             },
13968
13969             "tab" : function(e){
13970                 this.collapse();
13971                 
13972                 if(this.fireEvent("specialkey", this, e)){
13973                     this.onViewClick(false);
13974                 }
13975                 
13976                 return true;
13977             },
13978
13979             scope : this,
13980
13981             doRelay : function(foo, bar, hname){
13982                 if(hname == 'down' || this.scope.isExpanded()){
13983                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13984                 }
13985                 return true;
13986             },
13987
13988             forceKeyDown: true
13989         });
13990         
13991         
13992         this.queryDelay = Math.max(this.queryDelay || 10,
13993                 this.mode == 'local' ? 10 : 250);
13994         
13995         
13996         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13997         
13998         if(this.typeAhead){
13999             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14000         }
14001         if(this.editable !== false){
14002             this.inputEl().on("keyup", this.onKeyUp, this);
14003         }
14004         if(this.forceSelection){
14005             this.inputEl().on('blur', this.doForce, this);
14006         }
14007         
14008         if(this.multiple){
14009             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14010             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14011         }
14012     },
14013     
14014     initTickableEvents: function()
14015     {   
14016         this.createList();
14017         
14018         if(this.hiddenName){
14019             
14020             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14021             
14022             this.hiddenField.dom.value =
14023                 this.hiddenValue !== undefined ? this.hiddenValue :
14024                 this.value !== undefined ? this.value : '';
14025
14026             // prevent input submission
14027             this.el.dom.removeAttribute('name');
14028             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14029              
14030              
14031         }
14032         
14033 //        this.list = this.el.select('ul.dropdown-menu',true).first();
14034         
14035         this.choices = this.el.select('ul.roo-select2-choices', true).first();
14036         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14037         if(this.triggerList){
14038             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14039         }
14040          
14041         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14042         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14043         
14044         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14045         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14046         
14047         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14048         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14049         
14050         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14051         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14052         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14053         
14054         this.okBtn.hide();
14055         this.cancelBtn.hide();
14056         
14057         var _this = this;
14058         
14059         (function(){
14060             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14061             _this.list.setWidth(lw);
14062         }).defer(100);
14063         
14064         this.list.on('mouseover', this.onViewOver, this);
14065         this.list.on('mousemove', this.onViewMove, this);
14066         
14067         this.list.on('scroll', this.onViewScroll, this);
14068         
14069         if(!this.tpl){
14070             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
14071                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14072         }
14073
14074         this.view = new Roo.View(this.list, this.tpl, {
14075             singleSelect:true,
14076             tickable:true,
14077             parent:this,
14078             store: this.store,
14079             selectedClass: this.selectedClass
14080         });
14081         
14082         //this.view.wrapEl.setDisplayed(false);
14083         this.view.on('click', this.onViewClick, this);
14084         
14085         
14086         
14087         this.store.on('beforeload', this.onBeforeLoad, this);
14088         this.store.on('load', this.onLoad, this);
14089         this.store.on('loadexception', this.onLoadException, this);
14090         
14091         if(this.editable){
14092             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14093                 "up" : function(e){
14094                     this.inKeyMode = true;
14095                     this.selectPrev();
14096                 },
14097
14098                 "down" : function(e){
14099                     this.inKeyMode = true;
14100                     this.selectNext();
14101                 },
14102
14103                 "enter" : function(e){
14104                     if(this.fireEvent("specialkey", this, e)){
14105                         this.onViewClick(false);
14106                     }
14107                     
14108                     return true;
14109                 },
14110
14111                 "esc" : function(e){
14112                     this.onTickableFooterButtonClick(e, false, false);
14113                 },
14114
14115                 "tab" : function(e){
14116                     this.fireEvent("specialkey", this, e);
14117                     
14118                     this.onTickableFooterButtonClick(e, false, false);
14119                     
14120                     return true;
14121                 },
14122
14123                 scope : this,
14124
14125                 doRelay : function(e, fn, key){
14126                     if(this.scope.isExpanded()){
14127                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14128                     }
14129                     return true;
14130                 },
14131
14132                 forceKeyDown: true
14133             });
14134         }
14135         
14136         this.queryDelay = Math.max(this.queryDelay || 10,
14137                 this.mode == 'local' ? 10 : 250);
14138         
14139         
14140         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14141         
14142         if(this.typeAhead){
14143             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14144         }
14145         
14146         if(this.editable !== false){
14147             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14148         }
14149         
14150         this.indicator = this.indicatorEl();
14151         
14152         if(this.indicator){
14153             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14154             this.indicator.hide();
14155         }
14156         
14157     },
14158
14159     onDestroy : function(){
14160         if(this.view){
14161             this.view.setStore(null);
14162             this.view.el.removeAllListeners();
14163             this.view.el.remove();
14164             this.view.purgeListeners();
14165         }
14166         if(this.list){
14167             this.list.dom.innerHTML  = '';
14168         }
14169         
14170         if(this.store){
14171             this.store.un('beforeload', this.onBeforeLoad, this);
14172             this.store.un('load', this.onLoad, this);
14173             this.store.un('loadexception', this.onLoadException, this);
14174         }
14175         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14176     },
14177
14178     // private
14179     fireKey : function(e){
14180         if(e.isNavKeyPress() && !this.list.isVisible()){
14181             this.fireEvent("specialkey", this, e);
14182         }
14183     },
14184
14185     // private
14186     onResize: function(w, h){
14187 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14188 //        
14189 //        if(typeof w != 'number'){
14190 //            // we do not handle it!?!?
14191 //            return;
14192 //        }
14193 //        var tw = this.trigger.getWidth();
14194 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14195 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14196 //        var x = w - tw;
14197 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14198 //            
14199 //        //this.trigger.setStyle('left', x+'px');
14200 //        
14201 //        if(this.list && this.listWidth === undefined){
14202 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14203 //            this.list.setWidth(lw);
14204 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14205 //        }
14206         
14207     
14208         
14209     },
14210
14211     /**
14212      * Allow or prevent the user from directly editing the field text.  If false is passed,
14213      * the user will only be able to select from the items defined in the dropdown list.  This method
14214      * is the runtime equivalent of setting the 'editable' config option at config time.
14215      * @param {Boolean} value True to allow the user to directly edit the field text
14216      */
14217     setEditable : function(value){
14218         if(value == this.editable){
14219             return;
14220         }
14221         this.editable = value;
14222         if(!value){
14223             this.inputEl().dom.setAttribute('readOnly', true);
14224             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14225             this.inputEl().addClass('x-combo-noedit');
14226         }else{
14227             this.inputEl().dom.setAttribute('readOnly', false);
14228             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14229             this.inputEl().removeClass('x-combo-noedit');
14230         }
14231     },
14232
14233     // private
14234     
14235     onBeforeLoad : function(combo,opts){
14236         if(!this.hasFocus){
14237             return;
14238         }
14239          if (!opts.add) {
14240             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14241          }
14242         this.restrictHeight();
14243         this.selectedIndex = -1;
14244     },
14245
14246     // private
14247     onLoad : function(){
14248         
14249         this.hasQuery = false;
14250         
14251         if(!this.hasFocus){
14252             return;
14253         }
14254         
14255         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14256             this.loading.hide();
14257         }
14258         
14259         if(this.store.getCount() > 0){
14260             
14261             this.expand();
14262             this.restrictHeight();
14263             if(this.lastQuery == this.allQuery){
14264                 if(this.editable && !this.tickable){
14265                     this.inputEl().dom.select();
14266                 }
14267                 
14268                 if(
14269                     !this.selectByValue(this.value, true) &&
14270                     this.autoFocus && 
14271                     (
14272                         !this.store.lastOptions ||
14273                         typeof(this.store.lastOptions.add) == 'undefined' || 
14274                         this.store.lastOptions.add != true
14275                     )
14276                 ){
14277                     this.select(0, true);
14278                 }
14279             }else{
14280                 if(this.autoFocus){
14281                     this.selectNext();
14282                 }
14283                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14284                     this.taTask.delay(this.typeAheadDelay);
14285                 }
14286             }
14287         }else{
14288             this.onEmptyResults();
14289         }
14290         
14291         //this.el.focus();
14292     },
14293     // private
14294     onLoadException : function()
14295     {
14296         this.hasQuery = false;
14297         
14298         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14299             this.loading.hide();
14300         }
14301         
14302         if(this.tickable && this.editable){
14303             return;
14304         }
14305         
14306         this.collapse();
14307         // only causes errors at present
14308         //Roo.log(this.store.reader.jsonData);
14309         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14310             // fixme
14311             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14312         //}
14313         
14314         
14315     },
14316     // private
14317     onTypeAhead : function(){
14318         if(this.store.getCount() > 0){
14319             var r = this.store.getAt(0);
14320             var newValue = r.data[this.displayField];
14321             var len = newValue.length;
14322             var selStart = this.getRawValue().length;
14323             
14324             if(selStart != len){
14325                 this.setRawValue(newValue);
14326                 this.selectText(selStart, newValue.length);
14327             }
14328         }
14329     },
14330
14331     // private
14332     onSelect : function(record, index){
14333         
14334         if(this.fireEvent('beforeselect', this, record, index) !== false){
14335         
14336             this.setFromData(index > -1 ? record.data : false);
14337             
14338             this.collapse();
14339             this.fireEvent('select', this, record, index);
14340         }
14341     },
14342
14343     /**
14344      * Returns the currently selected field value or empty string if no value is set.
14345      * @return {String} value The selected value
14346      */
14347     getValue : function()
14348     {
14349         if(Roo.isIOS && this.useNativeIOS){
14350             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14351         }
14352         
14353         if(this.multiple){
14354             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14355         }
14356         
14357         if(this.valueField){
14358             return typeof this.value != 'undefined' ? this.value : '';
14359         }else{
14360             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14361         }
14362     },
14363     
14364     getRawValue : function()
14365     {
14366         if(Roo.isIOS && this.useNativeIOS){
14367             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14368         }
14369         
14370         var v = this.inputEl().getValue();
14371         
14372         return v;
14373     },
14374
14375     /**
14376      * Clears any text/value currently set in the field
14377      */
14378     clearValue : function(){
14379         
14380         if(this.hiddenField){
14381             this.hiddenField.dom.value = '';
14382         }
14383         this.value = '';
14384         this.setRawValue('');
14385         this.lastSelectionText = '';
14386         this.lastData = false;
14387         
14388         var close = this.closeTriggerEl();
14389         
14390         if(close){
14391             close.hide();
14392         }
14393         
14394         this.validate();
14395         
14396     },
14397
14398     /**
14399      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14400      * will be displayed in the field.  If the value does not match the data value of an existing item,
14401      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14402      * Otherwise the field will be blank (although the value will still be set).
14403      * @param {String} value The value to match
14404      */
14405     setValue : function(v)
14406     {
14407         if(Roo.isIOS && this.useNativeIOS){
14408             this.setIOSValue(v);
14409             return;
14410         }
14411         
14412         if(this.multiple){
14413             this.syncValue();
14414             return;
14415         }
14416         
14417         var text = v;
14418         if(this.valueField){
14419             var r = this.findRecord(this.valueField, v);
14420             if(r){
14421                 text = r.data[this.displayField];
14422             }else if(this.valueNotFoundText !== undefined){
14423                 text = this.valueNotFoundText;
14424             }
14425         }
14426         this.lastSelectionText = text;
14427         if(this.hiddenField){
14428             this.hiddenField.dom.value = v;
14429         }
14430         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14431         this.value = v;
14432         
14433         var close = this.closeTriggerEl();
14434         
14435         if(close){
14436             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14437         }
14438         
14439         this.validate();
14440     },
14441     /**
14442      * @property {Object} the last set data for the element
14443      */
14444     
14445     lastData : false,
14446     /**
14447      * Sets the value of the field based on a object which is related to the record format for the store.
14448      * @param {Object} value the value to set as. or false on reset?
14449      */
14450     setFromData : function(o){
14451         
14452         if(this.multiple){
14453             this.addItem(o);
14454             return;
14455         }
14456             
14457         var dv = ''; // display value
14458         var vv = ''; // value value..
14459         this.lastData = o;
14460         if (this.displayField) {
14461             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14462         } else {
14463             // this is an error condition!!!
14464             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14465         }
14466         
14467         if(this.valueField){
14468             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14469         }
14470         
14471         var close = this.closeTriggerEl();
14472         
14473         if(close){
14474             if(dv.length || vv * 1 > 0){
14475                 close.show() ;
14476                 this.blockFocus=true;
14477             } else {
14478                 close.hide();
14479             }             
14480         }
14481         
14482         if(this.hiddenField){
14483             this.hiddenField.dom.value = vv;
14484             
14485             this.lastSelectionText = dv;
14486             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14487             this.value = vv;
14488             return;
14489         }
14490         // no hidden field.. - we store the value in 'value', but still display
14491         // display field!!!!
14492         this.lastSelectionText = dv;
14493         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14494         this.value = vv;
14495         
14496         
14497         
14498     },
14499     // private
14500     reset : function(){
14501         // overridden so that last data is reset..
14502         
14503         if(this.multiple){
14504             this.clearItem();
14505             return;
14506         }
14507         
14508         this.setValue(this.originalValue);
14509         //this.clearInvalid();
14510         this.lastData = false;
14511         if (this.view) {
14512             this.view.clearSelections();
14513         }
14514         
14515         this.validate();
14516     },
14517     // private
14518     findRecord : function(prop, value){
14519         var record;
14520         if(this.store.getCount() > 0){
14521             this.store.each(function(r){
14522                 if(r.data[prop] == value){
14523                     record = r;
14524                     return false;
14525                 }
14526                 return true;
14527             });
14528         }
14529         return record;
14530     },
14531     
14532     getName: function()
14533     {
14534         // returns hidden if it's set..
14535         if (!this.rendered) {return ''};
14536         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14537         
14538     },
14539     // private
14540     onViewMove : function(e, t){
14541         this.inKeyMode = false;
14542     },
14543
14544     // private
14545     onViewOver : function(e, t){
14546         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14547             return;
14548         }
14549         var item = this.view.findItemFromChild(t);
14550         
14551         if(item){
14552             var index = this.view.indexOf(item);
14553             this.select(index, false);
14554         }
14555     },
14556
14557     // private
14558     onViewClick : function(view, doFocus, el, e)
14559     {
14560         var index = this.view.getSelectedIndexes()[0];
14561         
14562         var r = this.store.getAt(index);
14563         
14564         if(this.tickable){
14565             
14566             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14567                 return;
14568             }
14569             
14570             var rm = false;
14571             var _this = this;
14572             
14573             Roo.each(this.tickItems, function(v,k){
14574                 
14575                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14576                     Roo.log(v);
14577                     _this.tickItems.splice(k, 1);
14578                     
14579                     if(typeof(e) == 'undefined' && view == false){
14580                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14581                     }
14582                     
14583                     rm = true;
14584                     return;
14585                 }
14586             });
14587             
14588             if(rm){
14589                 return;
14590             }
14591             
14592             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14593                 this.tickItems.push(r.data);
14594             }
14595             
14596             if(typeof(e) == 'undefined' && view == false){
14597                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14598             }
14599                     
14600             return;
14601         }
14602         
14603         if(r){
14604             this.onSelect(r, index);
14605         }
14606         if(doFocus !== false && !this.blockFocus){
14607             this.inputEl().focus();
14608         }
14609     },
14610
14611     // private
14612     restrictHeight : function(){
14613         //this.innerList.dom.style.height = '';
14614         //var inner = this.innerList.dom;
14615         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14616         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14617         //this.list.beginUpdate();
14618         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14619         this.list.alignTo(this.inputEl(), this.listAlign);
14620         this.list.alignTo(this.inputEl(), this.listAlign);
14621         //this.list.endUpdate();
14622     },
14623
14624     // private
14625     onEmptyResults : function(){
14626         
14627         if(this.tickable && this.editable){
14628             this.hasFocus = false;
14629             this.restrictHeight();
14630             return;
14631         }
14632         
14633         this.collapse();
14634     },
14635
14636     /**
14637      * Returns true if the dropdown list is expanded, else false.
14638      */
14639     isExpanded : function(){
14640         return this.list.isVisible();
14641     },
14642
14643     /**
14644      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14645      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14646      * @param {String} value The data value of the item to select
14647      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14648      * selected item if it is not currently in view (defaults to true)
14649      * @return {Boolean} True if the value matched an item in the list, else false
14650      */
14651     selectByValue : function(v, scrollIntoView){
14652         if(v !== undefined && v !== null){
14653             var r = this.findRecord(this.valueField || this.displayField, v);
14654             if(r){
14655                 this.select(this.store.indexOf(r), scrollIntoView);
14656                 return true;
14657             }
14658         }
14659         return false;
14660     },
14661
14662     /**
14663      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14664      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14665      * @param {Number} index The zero-based index of the list item to select
14666      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14667      * selected item if it is not currently in view (defaults to true)
14668      */
14669     select : function(index, scrollIntoView){
14670         this.selectedIndex = index;
14671         this.view.select(index);
14672         if(scrollIntoView !== false){
14673             var el = this.view.getNode(index);
14674             /*
14675              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14676              */
14677             if(el){
14678                 this.list.scrollChildIntoView(el, false);
14679             }
14680         }
14681     },
14682
14683     // private
14684     selectNext : function(){
14685         var ct = this.store.getCount();
14686         if(ct > 0){
14687             if(this.selectedIndex == -1){
14688                 this.select(0);
14689             }else if(this.selectedIndex < ct-1){
14690                 this.select(this.selectedIndex+1);
14691             }
14692         }
14693     },
14694
14695     // private
14696     selectPrev : function(){
14697         var ct = this.store.getCount();
14698         if(ct > 0){
14699             if(this.selectedIndex == -1){
14700                 this.select(0);
14701             }else if(this.selectedIndex != 0){
14702                 this.select(this.selectedIndex-1);
14703             }
14704         }
14705     },
14706
14707     // private
14708     onKeyUp : function(e){
14709         if(this.editable !== false && !e.isSpecialKey()){
14710             this.lastKey = e.getKey();
14711             this.dqTask.delay(this.queryDelay);
14712         }
14713     },
14714
14715     // private
14716     validateBlur : function(){
14717         return !this.list || !this.list.isVisible();   
14718     },
14719
14720     // private
14721     initQuery : function(){
14722         
14723         var v = this.getRawValue();
14724         
14725         if(this.tickable && this.editable){
14726             v = this.tickableInputEl().getValue();
14727         }
14728         
14729         this.doQuery(v);
14730     },
14731
14732     // private
14733     doForce : function(){
14734         if(this.inputEl().dom.value.length > 0){
14735             this.inputEl().dom.value =
14736                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14737              
14738         }
14739     },
14740
14741     /**
14742      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14743      * query allowing the query action to be canceled if needed.
14744      * @param {String} query The SQL query to execute
14745      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14746      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14747      * saved in the current store (defaults to false)
14748      */
14749     doQuery : function(q, forceAll){
14750         
14751         if(q === undefined || q === null){
14752             q = '';
14753         }
14754         var qe = {
14755             query: q,
14756             forceAll: forceAll,
14757             combo: this,
14758             cancel:false
14759         };
14760         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14761             return false;
14762         }
14763         q = qe.query;
14764         
14765         forceAll = qe.forceAll;
14766         if(forceAll === true || (q.length >= this.minChars)){
14767             
14768             this.hasQuery = true;
14769             
14770             if(this.lastQuery != q || this.alwaysQuery){
14771                 this.lastQuery = q;
14772                 if(this.mode == 'local'){
14773                     this.selectedIndex = -1;
14774                     if(forceAll){
14775                         this.store.clearFilter();
14776                     }else{
14777                         
14778                         if(this.specialFilter){
14779                             this.fireEvent('specialfilter', this);
14780                             this.onLoad();
14781                             return;
14782                         }
14783                         
14784                         this.store.filter(this.displayField, q);
14785                     }
14786                     
14787                     this.store.fireEvent("datachanged", this.store);
14788                     
14789                     this.onLoad();
14790                     
14791                     
14792                 }else{
14793                     
14794                     this.store.baseParams[this.queryParam] = q;
14795                     
14796                     var options = {params : this.getParams(q)};
14797                     
14798                     if(this.loadNext){
14799                         options.add = true;
14800                         options.params.start = this.page * this.pageSize;
14801                     }
14802                     
14803                     this.store.load(options);
14804                     
14805                     /*
14806                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14807                      *  we should expand the list on onLoad
14808                      *  so command out it
14809                      */
14810 //                    this.expand();
14811                 }
14812             }else{
14813                 this.selectedIndex = -1;
14814                 this.onLoad();   
14815             }
14816         }
14817         
14818         this.loadNext = false;
14819     },
14820     
14821     // private
14822     getParams : function(q){
14823         var p = {};
14824         //p[this.queryParam] = q;
14825         
14826         if(this.pageSize){
14827             p.start = 0;
14828             p.limit = this.pageSize;
14829         }
14830         return p;
14831     },
14832
14833     /**
14834      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14835      */
14836     collapse : function(){
14837         if(!this.isExpanded()){
14838             return;
14839         }
14840         
14841         this.list.hide();
14842         
14843         this.hasFocus = false;
14844         
14845         if(this.tickable){
14846             this.okBtn.hide();
14847             this.cancelBtn.hide();
14848             this.trigger.show();
14849             
14850             if(this.editable){
14851                 this.tickableInputEl().dom.value = '';
14852                 this.tickableInputEl().blur();
14853             }
14854             
14855         }
14856         
14857         Roo.get(document).un('mousedown', this.collapseIf, this);
14858         Roo.get(document).un('mousewheel', this.collapseIf, this);
14859         if (!this.editable) {
14860             Roo.get(document).un('keydown', this.listKeyPress, this);
14861         }
14862         this.fireEvent('collapse', this);
14863         
14864         this.validate();
14865     },
14866
14867     // private
14868     collapseIf : function(e){
14869         var in_combo  = e.within(this.el);
14870         var in_list =  e.within(this.list);
14871         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14872         
14873         if (in_combo || in_list || is_list) {
14874             //e.stopPropagation();
14875             return;
14876         }
14877         
14878         if(this.tickable){
14879             this.onTickableFooterButtonClick(e, false, false);
14880         }
14881
14882         this.collapse();
14883         
14884     },
14885
14886     /**
14887      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14888      */
14889     expand : function(){
14890        
14891         if(this.isExpanded() || !this.hasFocus){
14892             return;
14893         }
14894         
14895         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14896         this.list.setWidth(lw);
14897         
14898         Roo.log('expand');
14899         
14900         this.list.show();
14901         
14902         this.restrictHeight();
14903         
14904         if(this.tickable){
14905             
14906             this.tickItems = Roo.apply([], this.item);
14907             
14908             this.okBtn.show();
14909             this.cancelBtn.show();
14910             this.trigger.hide();
14911             
14912             if(this.editable){
14913                 this.tickableInputEl().focus();
14914             }
14915             
14916         }
14917         
14918         Roo.get(document).on('mousedown', this.collapseIf, this);
14919         Roo.get(document).on('mousewheel', this.collapseIf, this);
14920         if (!this.editable) {
14921             Roo.get(document).on('keydown', this.listKeyPress, this);
14922         }
14923         
14924         this.fireEvent('expand', this);
14925     },
14926
14927     // private
14928     // Implements the default empty TriggerField.onTriggerClick function
14929     onTriggerClick : function(e)
14930     {
14931         Roo.log('trigger click');
14932         
14933         if(this.disabled || !this.triggerList){
14934             return;
14935         }
14936         
14937         this.page = 0;
14938         this.loadNext = false;
14939         
14940         if(this.isExpanded()){
14941             this.collapse();
14942             if (!this.blockFocus) {
14943                 this.inputEl().focus();
14944             }
14945             
14946         }else {
14947             this.hasFocus = true;
14948             if(this.triggerAction == 'all') {
14949                 this.doQuery(this.allQuery, true);
14950             } else {
14951                 this.doQuery(this.getRawValue());
14952             }
14953             if (!this.blockFocus) {
14954                 this.inputEl().focus();
14955             }
14956         }
14957     },
14958     
14959     onTickableTriggerClick : function(e)
14960     {
14961         if(this.disabled){
14962             return;
14963         }
14964         
14965         this.page = 0;
14966         this.loadNext = false;
14967         this.hasFocus = true;
14968         
14969         if(this.triggerAction == 'all') {
14970             this.doQuery(this.allQuery, true);
14971         } else {
14972             this.doQuery(this.getRawValue());
14973         }
14974     },
14975     
14976     onSearchFieldClick : function(e)
14977     {
14978         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14979             this.onTickableFooterButtonClick(e, false, false);
14980             return;
14981         }
14982         
14983         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14984             return;
14985         }
14986         
14987         this.page = 0;
14988         this.loadNext = false;
14989         this.hasFocus = true;
14990         
14991         if(this.triggerAction == 'all') {
14992             this.doQuery(this.allQuery, true);
14993         } else {
14994             this.doQuery(this.getRawValue());
14995         }
14996     },
14997     
14998     listKeyPress : function(e)
14999     {
15000         //Roo.log('listkeypress');
15001         // scroll to first matching element based on key pres..
15002         if (e.isSpecialKey()) {
15003             return false;
15004         }
15005         var k = String.fromCharCode(e.getKey()).toUpperCase();
15006         //Roo.log(k);
15007         var match  = false;
15008         var csel = this.view.getSelectedNodes();
15009         var cselitem = false;
15010         if (csel.length) {
15011             var ix = this.view.indexOf(csel[0]);
15012             cselitem  = this.store.getAt(ix);
15013             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
15014                 cselitem = false;
15015             }
15016             
15017         }
15018         
15019         this.store.each(function(v) { 
15020             if (cselitem) {
15021                 // start at existing selection.
15022                 if (cselitem.id == v.id) {
15023                     cselitem = false;
15024                 }
15025                 return true;
15026             }
15027                 
15028             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15029                 match = this.store.indexOf(v);
15030                 return false;
15031             }
15032             return true;
15033         }, this);
15034         
15035         if (match === false) {
15036             return true; // no more action?
15037         }
15038         // scroll to?
15039         this.view.select(match);
15040         var sn = Roo.get(this.view.getSelectedNodes()[0]);
15041         sn.scrollIntoView(sn.dom.parentNode, false);
15042     },
15043     
15044     onViewScroll : function(e, t){
15045         
15046         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){
15047             return;
15048         }
15049         
15050         this.hasQuery = true;
15051         
15052         this.loading = this.list.select('.loading', true).first();
15053         
15054         if(this.loading === null){
15055             this.list.createChild({
15056                 tag: 'div',
15057                 cls: 'loading roo-select2-more-results roo-select2-active',
15058                 html: 'Loading more results...'
15059             });
15060             
15061             this.loading = this.list.select('.loading', true).first();
15062             
15063             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15064             
15065             this.loading.hide();
15066         }
15067         
15068         this.loading.show();
15069         
15070         var _combo = this;
15071         
15072         this.page++;
15073         this.loadNext = true;
15074         
15075         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15076         
15077         return;
15078     },
15079     
15080     addItem : function(o)
15081     {   
15082         var dv = ''; // display value
15083         
15084         if (this.displayField) {
15085             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15086         } else {
15087             // this is an error condition!!!
15088             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15089         }
15090         
15091         if(!dv.length){
15092             return;
15093         }
15094         
15095         var choice = this.choices.createChild({
15096             tag: 'li',
15097             cls: 'roo-select2-search-choice',
15098             cn: [
15099                 {
15100                     tag: 'div',
15101                     html: dv
15102                 },
15103                 {
15104                     tag: 'a',
15105                     href: '#',
15106                     cls: 'roo-select2-search-choice-close fa fa-times',
15107                     tabindex: '-1'
15108                 }
15109             ]
15110             
15111         }, this.searchField);
15112         
15113         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15114         
15115         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15116         
15117         this.item.push(o);
15118         
15119         this.lastData = o;
15120         
15121         this.syncValue();
15122         
15123         this.inputEl().dom.value = '';
15124         
15125         this.validate();
15126     },
15127     
15128     onRemoveItem : function(e, _self, o)
15129     {
15130         e.preventDefault();
15131         
15132         this.lastItem = Roo.apply([], this.item);
15133         
15134         var index = this.item.indexOf(o.data) * 1;
15135         
15136         if( index < 0){
15137             Roo.log('not this item?!');
15138             return;
15139         }
15140         
15141         this.item.splice(index, 1);
15142         o.item.remove();
15143         
15144         this.syncValue();
15145         
15146         this.fireEvent('remove', this, e);
15147         
15148         this.validate();
15149         
15150     },
15151     
15152     syncValue : function()
15153     {
15154         if(!this.item.length){
15155             this.clearValue();
15156             return;
15157         }
15158             
15159         var value = [];
15160         var _this = this;
15161         Roo.each(this.item, function(i){
15162             if(_this.valueField){
15163                 value.push(i[_this.valueField]);
15164                 return;
15165             }
15166
15167             value.push(i);
15168         });
15169
15170         this.value = value.join(',');
15171
15172         if(this.hiddenField){
15173             this.hiddenField.dom.value = this.value;
15174         }
15175         
15176         this.store.fireEvent("datachanged", this.store);
15177         
15178         this.validate();
15179     },
15180     
15181     clearItem : function()
15182     {
15183         if(!this.multiple){
15184             return;
15185         }
15186         
15187         this.item = [];
15188         
15189         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15190            c.remove();
15191         });
15192         
15193         this.syncValue();
15194         
15195         this.validate();
15196         
15197         if(this.tickable && !Roo.isTouch){
15198             this.view.refresh();
15199         }
15200     },
15201     
15202     inputEl: function ()
15203     {
15204         if(Roo.isIOS && this.useNativeIOS){
15205             return this.el.select('select.roo-ios-select', true).first();
15206         }
15207         
15208         if(Roo.isTouch && this.mobileTouchView){
15209             return this.el.select('input.form-control',true).first();
15210         }
15211         
15212         if(this.tickable){
15213             return this.searchField;
15214         }
15215         
15216         return this.el.select('input.form-control',true).first();
15217     },
15218     
15219     onTickableFooterButtonClick : function(e, btn, el)
15220     {
15221         e.preventDefault();
15222         
15223         this.lastItem = Roo.apply([], this.item);
15224         
15225         if(btn && btn.name == 'cancel'){
15226             this.tickItems = Roo.apply([], this.item);
15227             this.collapse();
15228             return;
15229         }
15230         
15231         this.clearItem();
15232         
15233         var _this = this;
15234         
15235         Roo.each(this.tickItems, function(o){
15236             _this.addItem(o);
15237         });
15238         
15239         this.collapse();
15240         
15241     },
15242     
15243     validate : function()
15244     {
15245         if(this.getVisibilityEl().hasClass('hidden')){
15246             return true;
15247         }
15248         
15249         var v = this.getRawValue();
15250         
15251         if(this.multiple){
15252             v = this.getValue();
15253         }
15254         
15255         if(this.disabled || this.allowBlank || v.length){
15256             this.markValid();
15257             return true;
15258         }
15259         
15260         this.markInvalid();
15261         return false;
15262     },
15263     
15264     tickableInputEl : function()
15265     {
15266         if(!this.tickable || !this.editable){
15267             return this.inputEl();
15268         }
15269         
15270         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15271     },
15272     
15273     
15274     getAutoCreateTouchView : function()
15275     {
15276         var id = Roo.id();
15277         
15278         var cfg = {
15279             cls: 'form-group' //input-group
15280         };
15281         
15282         var input =  {
15283             tag: 'input',
15284             id : id,
15285             type : this.inputType,
15286             cls : 'form-control x-combo-noedit',
15287             autocomplete: 'new-password',
15288             placeholder : this.placeholder || '',
15289             readonly : true
15290         };
15291         
15292         if (this.name) {
15293             input.name = this.name;
15294         }
15295         
15296         if (this.size) {
15297             input.cls += ' input-' + this.size;
15298         }
15299         
15300         if (this.disabled) {
15301             input.disabled = true;
15302         }
15303         
15304         var inputblock = {
15305             cls : '',
15306             cn : [
15307                 input
15308             ]
15309         };
15310         
15311         if(this.before){
15312             inputblock.cls += ' input-group';
15313             
15314             inputblock.cn.unshift({
15315                 tag :'span',
15316                 cls : 'input-group-addon input-group-prepend input-group-text',
15317                 html : this.before
15318             });
15319         }
15320         
15321         if(this.removable && !this.multiple){
15322             inputblock.cls += ' roo-removable';
15323             
15324             inputblock.cn.push({
15325                 tag: 'button',
15326                 html : 'x',
15327                 cls : 'roo-combo-removable-btn close'
15328             });
15329         }
15330
15331         if(this.hasFeedback && !this.allowBlank){
15332             
15333             inputblock.cls += ' has-feedback';
15334             
15335             inputblock.cn.push({
15336                 tag: 'span',
15337                 cls: 'glyphicon form-control-feedback'
15338             });
15339             
15340         }
15341         
15342         if (this.after) {
15343             
15344             inputblock.cls += (this.before) ? '' : ' input-group';
15345             
15346             inputblock.cn.push({
15347                 tag :'span',
15348                 cls : 'input-group-addon input-group-append input-group-text',
15349                 html : this.after
15350             });
15351         }
15352
15353         
15354         var ibwrap = inputblock;
15355         
15356         if(this.multiple){
15357             ibwrap = {
15358                 tag: 'ul',
15359                 cls: 'roo-select2-choices',
15360                 cn:[
15361                     {
15362                         tag: 'li',
15363                         cls: 'roo-select2-search-field',
15364                         cn: [
15365
15366                             inputblock
15367                         ]
15368                     }
15369                 ]
15370             };
15371         
15372             
15373         }
15374         
15375         var combobox = {
15376             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15377             cn: [
15378                 {
15379                     tag: 'input',
15380                     type : 'hidden',
15381                     cls: 'form-hidden-field'
15382                 },
15383                 ibwrap
15384             ]
15385         };
15386         
15387         if(!this.multiple && this.showToggleBtn){
15388             
15389             var caret = {
15390                 cls: 'caret'
15391             };
15392             
15393             if (this.caret != false) {
15394                 caret = {
15395                      tag: 'i',
15396                      cls: 'fa fa-' + this.caret
15397                 };
15398                 
15399             }
15400             
15401             combobox.cn.push({
15402                 tag :'span',
15403                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15404                 cn : [
15405                     Roo.bootstrap.version == 3 ? caret : '',
15406                     {
15407                         tag: 'span',
15408                         cls: 'combobox-clear',
15409                         cn  : [
15410                             {
15411                                 tag : 'i',
15412                                 cls: 'icon-remove'
15413                             }
15414                         ]
15415                     }
15416                 ]
15417
15418             })
15419         }
15420         
15421         if(this.multiple){
15422             combobox.cls += ' roo-select2-container-multi';
15423         }
15424         
15425         var align = this.labelAlign || this.parentLabelAlign();
15426         
15427         if (align ==='left' && this.fieldLabel.length) {
15428
15429             cfg.cn = [
15430                 {
15431                    tag : 'i',
15432                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15433                    tooltip : 'This field is required'
15434                 },
15435                 {
15436                     tag: 'label',
15437                     cls : 'control-label col-form-label',
15438                     html : this.fieldLabel
15439
15440                 },
15441                 {
15442                     cls : '', 
15443                     cn: [
15444                         combobox
15445                     ]
15446                 }
15447             ];
15448             
15449             var labelCfg = cfg.cn[1];
15450             var contentCfg = cfg.cn[2];
15451             
15452
15453             if(this.indicatorpos == 'right'){
15454                 cfg.cn = [
15455                     {
15456                         tag: 'label',
15457                         'for' :  id,
15458                         cls : 'control-label col-form-label',
15459                         cn : [
15460                             {
15461                                 tag : 'span',
15462                                 html : this.fieldLabel
15463                             },
15464                             {
15465                                 tag : 'i',
15466                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15467                                 tooltip : 'This field is required'
15468                             }
15469                         ]
15470                     },
15471                     {
15472                         cls : "",
15473                         cn: [
15474                             combobox
15475                         ]
15476                     }
15477
15478                 ];
15479                 
15480                 labelCfg = cfg.cn[0];
15481                 contentCfg = cfg.cn[1];
15482             }
15483             
15484            
15485             
15486             if(this.labelWidth > 12){
15487                 labelCfg.style = "width: " + this.labelWidth + 'px';
15488             }
15489             
15490             if(this.labelWidth < 13 && this.labelmd == 0){
15491                 this.labelmd = this.labelWidth;
15492             }
15493             
15494             if(this.labellg > 0){
15495                 labelCfg.cls += ' col-lg-' + this.labellg;
15496                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15497             }
15498             
15499             if(this.labelmd > 0){
15500                 labelCfg.cls += ' col-md-' + this.labelmd;
15501                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15502             }
15503             
15504             if(this.labelsm > 0){
15505                 labelCfg.cls += ' col-sm-' + this.labelsm;
15506                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15507             }
15508             
15509             if(this.labelxs > 0){
15510                 labelCfg.cls += ' col-xs-' + this.labelxs;
15511                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15512             }
15513                 
15514                 
15515         } else if ( this.fieldLabel.length) {
15516             cfg.cn = [
15517                 {
15518                    tag : 'i',
15519                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15520                    tooltip : 'This field is required'
15521                 },
15522                 {
15523                     tag: 'label',
15524                     cls : 'control-label',
15525                     html : this.fieldLabel
15526
15527                 },
15528                 {
15529                     cls : '', 
15530                     cn: [
15531                         combobox
15532                     ]
15533                 }
15534             ];
15535             
15536             if(this.indicatorpos == 'right'){
15537                 cfg.cn = [
15538                     {
15539                         tag: 'label',
15540                         cls : 'control-label',
15541                         html : this.fieldLabel,
15542                         cn : [
15543                             {
15544                                tag : 'i',
15545                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15546                                tooltip : 'This field is required'
15547                             }
15548                         ]
15549                     },
15550                     {
15551                         cls : '', 
15552                         cn: [
15553                             combobox
15554                         ]
15555                     }
15556                 ];
15557             }
15558         } else {
15559             cfg.cn = combobox;    
15560         }
15561         
15562         
15563         var settings = this;
15564         
15565         ['xs','sm','md','lg'].map(function(size){
15566             if (settings[size]) {
15567                 cfg.cls += ' col-' + size + '-' + settings[size];
15568             }
15569         });
15570         
15571         return cfg;
15572     },
15573     
15574     initTouchView : function()
15575     {
15576         this.renderTouchView();
15577         
15578         this.touchViewEl.on('scroll', function(){
15579             this.el.dom.scrollTop = 0;
15580         }, this);
15581         
15582         this.originalValue = this.getValue();
15583         
15584         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15585         
15586         this.inputEl().on("click", this.showTouchView, this);
15587         if (this.triggerEl) {
15588             this.triggerEl.on("click", this.showTouchView, this);
15589         }
15590         
15591         
15592         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15593         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15594         
15595         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15596         
15597         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15598         this.store.on('load', this.onTouchViewLoad, this);
15599         this.store.on('loadexception', this.onTouchViewLoadException, this);
15600         
15601         if(this.hiddenName){
15602             
15603             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15604             
15605             this.hiddenField.dom.value =
15606                 this.hiddenValue !== undefined ? this.hiddenValue :
15607                 this.value !== undefined ? this.value : '';
15608         
15609             this.el.dom.removeAttribute('name');
15610             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15611         }
15612         
15613         if(this.multiple){
15614             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15615             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15616         }
15617         
15618         if(this.removable && !this.multiple){
15619             var close = this.closeTriggerEl();
15620             if(close){
15621                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15622                 close.on('click', this.removeBtnClick, this, close);
15623             }
15624         }
15625         /*
15626          * fix the bug in Safari iOS8
15627          */
15628         this.inputEl().on("focus", function(e){
15629             document.activeElement.blur();
15630         }, this);
15631         
15632         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15633         
15634         return;
15635         
15636         
15637     },
15638     
15639     renderTouchView : function()
15640     {
15641         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15642         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15643         
15644         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15645         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15646         
15647         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15648         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15649         this.touchViewBodyEl.setStyle('overflow', 'auto');
15650         
15651         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15652         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15653         
15654         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15655         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15656         
15657     },
15658     
15659     showTouchView : function()
15660     {
15661         if(this.disabled){
15662             return;
15663         }
15664         
15665         this.touchViewHeaderEl.hide();
15666
15667         if(this.modalTitle.length){
15668             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15669             this.touchViewHeaderEl.show();
15670         }
15671
15672         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15673         this.touchViewEl.show();
15674
15675         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15676         
15677         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15678         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15679
15680         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15681
15682         if(this.modalTitle.length){
15683             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15684         }
15685         
15686         this.touchViewBodyEl.setHeight(bodyHeight);
15687
15688         if(this.animate){
15689             var _this = this;
15690             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15691         }else{
15692             this.touchViewEl.addClass('in');
15693         }
15694         
15695         if(this._touchViewMask){
15696             Roo.get(document.body).addClass("x-body-masked");
15697             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15698             this._touchViewMask.setStyle('z-index', 10000);
15699             this._touchViewMask.addClass('show');
15700         }
15701         
15702         this.doTouchViewQuery();
15703         
15704     },
15705     
15706     hideTouchView : function()
15707     {
15708         this.touchViewEl.removeClass('in');
15709
15710         if(this.animate){
15711             var _this = this;
15712             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15713         }else{
15714             this.touchViewEl.setStyle('display', 'none');
15715         }
15716         
15717         if(this._touchViewMask){
15718             this._touchViewMask.removeClass('show');
15719             Roo.get(document.body).removeClass("x-body-masked");
15720         }
15721     },
15722     
15723     setTouchViewValue : function()
15724     {
15725         if(this.multiple){
15726             this.clearItem();
15727         
15728             var _this = this;
15729
15730             Roo.each(this.tickItems, function(o){
15731                 this.addItem(o);
15732             }, this);
15733         }
15734         
15735         this.hideTouchView();
15736     },
15737     
15738     doTouchViewQuery : function()
15739     {
15740         var qe = {
15741             query: '',
15742             forceAll: true,
15743             combo: this,
15744             cancel:false
15745         };
15746         
15747         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15748             return false;
15749         }
15750         
15751         if(!this.alwaysQuery || this.mode == 'local'){
15752             this.onTouchViewLoad();
15753             return;
15754         }
15755         
15756         this.store.load();
15757     },
15758     
15759     onTouchViewBeforeLoad : function(combo,opts)
15760     {
15761         return;
15762     },
15763
15764     // private
15765     onTouchViewLoad : function()
15766     {
15767         if(this.store.getCount() < 1){
15768             this.onTouchViewEmptyResults();
15769             return;
15770         }
15771         
15772         this.clearTouchView();
15773         
15774         var rawValue = this.getRawValue();
15775         
15776         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15777         
15778         this.tickItems = [];
15779         
15780         this.store.data.each(function(d, rowIndex){
15781             var row = this.touchViewListGroup.createChild(template);
15782             
15783             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15784                 row.addClass(d.data.cls);
15785             }
15786             
15787             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15788                 var cfg = {
15789                     data : d.data,
15790                     html : d.data[this.displayField]
15791                 };
15792                 
15793                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15794                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15795                 }
15796             }
15797             row.removeClass('selected');
15798             if(!this.multiple && this.valueField &&
15799                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15800             {
15801                 // radio buttons..
15802                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15803                 row.addClass('selected');
15804             }
15805             
15806             if(this.multiple && this.valueField &&
15807                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15808             {
15809                 
15810                 // checkboxes...
15811                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15812                 this.tickItems.push(d.data);
15813             }
15814             
15815             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15816             
15817         }, this);
15818         
15819         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15820         
15821         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15822
15823         if(this.modalTitle.length){
15824             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15825         }
15826
15827         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15828         
15829         if(this.mobile_restrict_height && listHeight < bodyHeight){
15830             this.touchViewBodyEl.setHeight(listHeight);
15831         }
15832         
15833         var _this = this;
15834         
15835         if(firstChecked && listHeight > bodyHeight){
15836             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15837         }
15838         
15839     },
15840     
15841     onTouchViewLoadException : function()
15842     {
15843         this.hideTouchView();
15844     },
15845     
15846     onTouchViewEmptyResults : function()
15847     {
15848         this.clearTouchView();
15849         
15850         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15851         
15852         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15853         
15854     },
15855     
15856     clearTouchView : function()
15857     {
15858         this.touchViewListGroup.dom.innerHTML = '';
15859     },
15860     
15861     onTouchViewClick : function(e, el, o)
15862     {
15863         e.preventDefault();
15864         
15865         var row = o.row;
15866         var rowIndex = o.rowIndex;
15867         
15868         var r = this.store.getAt(rowIndex);
15869         
15870         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15871             
15872             if(!this.multiple){
15873                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15874                     c.dom.removeAttribute('checked');
15875                 }, this);
15876
15877                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15878
15879                 this.setFromData(r.data);
15880
15881                 var close = this.closeTriggerEl();
15882
15883                 if(close){
15884                     close.show();
15885                 }
15886
15887                 this.hideTouchView();
15888
15889                 this.fireEvent('select', this, r, rowIndex);
15890
15891                 return;
15892             }
15893
15894             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15895                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15896                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15897                 return;
15898             }
15899
15900             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15901             this.addItem(r.data);
15902             this.tickItems.push(r.data);
15903         }
15904     },
15905     
15906     getAutoCreateNativeIOS : function()
15907     {
15908         var cfg = {
15909             cls: 'form-group' //input-group,
15910         };
15911         
15912         var combobox =  {
15913             tag: 'select',
15914             cls : 'roo-ios-select'
15915         };
15916         
15917         if (this.name) {
15918             combobox.name = this.name;
15919         }
15920         
15921         if (this.disabled) {
15922             combobox.disabled = true;
15923         }
15924         
15925         var settings = this;
15926         
15927         ['xs','sm','md','lg'].map(function(size){
15928             if (settings[size]) {
15929                 cfg.cls += ' col-' + size + '-' + settings[size];
15930             }
15931         });
15932         
15933         cfg.cn = combobox;
15934         
15935         return cfg;
15936         
15937     },
15938     
15939     initIOSView : function()
15940     {
15941         this.store.on('load', this.onIOSViewLoad, this);
15942         
15943         return;
15944     },
15945     
15946     onIOSViewLoad : function()
15947     {
15948         if(this.store.getCount() < 1){
15949             return;
15950         }
15951         
15952         this.clearIOSView();
15953         
15954         if(this.allowBlank) {
15955             
15956             var default_text = '-- SELECT --';
15957             
15958             if(this.placeholder.length){
15959                 default_text = this.placeholder;
15960             }
15961             
15962             if(this.emptyTitle.length){
15963                 default_text += ' - ' + this.emptyTitle + ' -';
15964             }
15965             
15966             var opt = this.inputEl().createChild({
15967                 tag: 'option',
15968                 value : 0,
15969                 html : default_text
15970             });
15971             
15972             var o = {};
15973             o[this.valueField] = 0;
15974             o[this.displayField] = default_text;
15975             
15976             this.ios_options.push({
15977                 data : o,
15978                 el : opt
15979             });
15980             
15981         }
15982         
15983         this.store.data.each(function(d, rowIndex){
15984             
15985             var html = '';
15986             
15987             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15988                 html = d.data[this.displayField];
15989             }
15990             
15991             var value = '';
15992             
15993             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15994                 value = d.data[this.valueField];
15995             }
15996             
15997             var option = {
15998                 tag: 'option',
15999                 value : value,
16000                 html : html
16001             };
16002             
16003             if(this.value == d.data[this.valueField]){
16004                 option['selected'] = true;
16005             }
16006             
16007             var opt = this.inputEl().createChild(option);
16008             
16009             this.ios_options.push({
16010                 data : d.data,
16011                 el : opt
16012             });
16013             
16014         }, this);
16015         
16016         this.inputEl().on('change', function(){
16017            this.fireEvent('select', this);
16018         }, this);
16019         
16020     },
16021     
16022     clearIOSView: function()
16023     {
16024         this.inputEl().dom.innerHTML = '';
16025         
16026         this.ios_options = [];
16027     },
16028     
16029     setIOSValue: function(v)
16030     {
16031         this.value = v;
16032         
16033         if(!this.ios_options){
16034             return;
16035         }
16036         
16037         Roo.each(this.ios_options, function(opts){
16038            
16039            opts.el.dom.removeAttribute('selected');
16040            
16041            if(opts.data[this.valueField] != v){
16042                return;
16043            }
16044            
16045            opts.el.dom.setAttribute('selected', true);
16046            
16047         }, this);
16048     }
16049
16050     /** 
16051     * @cfg {Boolean} grow 
16052     * @hide 
16053     */
16054     /** 
16055     * @cfg {Number} growMin 
16056     * @hide 
16057     */
16058     /** 
16059     * @cfg {Number} growMax 
16060     * @hide 
16061     */
16062     /**
16063      * @hide
16064      * @method autoSize
16065      */
16066 });
16067
16068 Roo.apply(Roo.bootstrap.ComboBox,  {
16069     
16070     header : {
16071         tag: 'div',
16072         cls: 'modal-header',
16073         cn: [
16074             {
16075                 tag: 'h4',
16076                 cls: 'modal-title'
16077             }
16078         ]
16079     },
16080     
16081     body : {
16082         tag: 'div',
16083         cls: 'modal-body',
16084         cn: [
16085             {
16086                 tag: 'ul',
16087                 cls: 'list-group'
16088             }
16089         ]
16090     },
16091     
16092     listItemRadio : {
16093         tag: 'li',
16094         cls: 'list-group-item',
16095         cn: [
16096             {
16097                 tag: 'span',
16098                 cls: 'roo-combobox-list-group-item-value'
16099             },
16100             {
16101                 tag: 'div',
16102                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16103                 cn: [
16104                     {
16105                         tag: 'input',
16106                         type: 'radio'
16107                     },
16108                     {
16109                         tag: 'label'
16110                     }
16111                 ]
16112             }
16113         ]
16114     },
16115     
16116     listItemCheckbox : {
16117         tag: 'li',
16118         cls: 'list-group-item',
16119         cn: [
16120             {
16121                 tag: 'span',
16122                 cls: 'roo-combobox-list-group-item-value'
16123             },
16124             {
16125                 tag: 'div',
16126                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16127                 cn: [
16128                     {
16129                         tag: 'input',
16130                         type: 'checkbox'
16131                     },
16132                     {
16133                         tag: 'label'
16134                     }
16135                 ]
16136             }
16137         ]
16138     },
16139     
16140     emptyResult : {
16141         tag: 'div',
16142         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16143     },
16144     
16145     footer : {
16146         tag: 'div',
16147         cls: 'modal-footer',
16148         cn: [
16149             {
16150                 tag: 'div',
16151                 cls: 'row',
16152                 cn: [
16153                     {
16154                         tag: 'div',
16155                         cls: 'col-xs-6 text-left',
16156                         cn: {
16157                             tag: 'button',
16158                             cls: 'btn btn-danger roo-touch-view-cancel',
16159                             html: 'Cancel'
16160                         }
16161                     },
16162                     {
16163                         tag: 'div',
16164                         cls: 'col-xs-6 text-right',
16165                         cn: {
16166                             tag: 'button',
16167                             cls: 'btn btn-success roo-touch-view-ok',
16168                             html: 'OK'
16169                         }
16170                     }
16171                 ]
16172             }
16173         ]
16174         
16175     }
16176 });
16177
16178 Roo.apply(Roo.bootstrap.ComboBox,  {
16179     
16180     touchViewTemplate : {
16181         tag: 'div',
16182         cls: 'modal fade roo-combobox-touch-view',
16183         cn: [
16184             {
16185                 tag: 'div',
16186                 cls: 'modal-dialog',
16187                 style : 'position:fixed', // we have to fix position....
16188                 cn: [
16189                     {
16190                         tag: 'div',
16191                         cls: 'modal-content',
16192                         cn: [
16193                             Roo.bootstrap.ComboBox.header,
16194                             Roo.bootstrap.ComboBox.body,
16195                             Roo.bootstrap.ComboBox.footer
16196                         ]
16197                     }
16198                 ]
16199             }
16200         ]
16201     }
16202 });/*
16203  * Based on:
16204  * Ext JS Library 1.1.1
16205  * Copyright(c) 2006-2007, Ext JS, LLC.
16206  *
16207  * Originally Released Under LGPL - original licence link has changed is not relivant.
16208  *
16209  * Fork - LGPL
16210  * <script type="text/javascript">
16211  */
16212
16213 /**
16214  * @class Roo.View
16215  * @extends Roo.util.Observable
16216  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16217  * This class also supports single and multi selection modes. <br>
16218  * Create a data model bound view:
16219  <pre><code>
16220  var store = new Roo.data.Store(...);
16221
16222  var view = new Roo.View({
16223     el : "my-element",
16224     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16225  
16226     singleSelect: true,
16227     selectedClass: "ydataview-selected",
16228     store: store
16229  });
16230
16231  // listen for node click?
16232  view.on("click", function(vw, index, node, e){
16233  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16234  });
16235
16236  // load XML data
16237  dataModel.load("foobar.xml");
16238  </code></pre>
16239  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16240  * <br><br>
16241  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16242  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16243  * 
16244  * Note: old style constructor is still suported (container, template, config)
16245  * 
16246  * @constructor
16247  * Create a new View
16248  * @param {Object} config The config object
16249  * 
16250  */
16251 Roo.View = function(config, depreciated_tpl, depreciated_config){
16252     
16253     this.parent = false;
16254     
16255     if (typeof(depreciated_tpl) == 'undefined') {
16256         // new way.. - universal constructor.
16257         Roo.apply(this, config);
16258         this.el  = Roo.get(this.el);
16259     } else {
16260         // old format..
16261         this.el  = Roo.get(config);
16262         this.tpl = depreciated_tpl;
16263         Roo.apply(this, depreciated_config);
16264     }
16265     this.wrapEl  = this.el.wrap().wrap();
16266     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16267     
16268     
16269     if(typeof(this.tpl) == "string"){
16270         this.tpl = new Roo.Template(this.tpl);
16271     } else {
16272         // support xtype ctors..
16273         this.tpl = new Roo.factory(this.tpl, Roo);
16274     }
16275     
16276     
16277     this.tpl.compile();
16278     
16279     /** @private */
16280     this.addEvents({
16281         /**
16282          * @event beforeclick
16283          * Fires before a click is processed. Returns false to cancel the default action.
16284          * @param {Roo.View} this
16285          * @param {Number} index The index of the target node
16286          * @param {HTMLElement} node The target node
16287          * @param {Roo.EventObject} e The raw event object
16288          */
16289             "beforeclick" : true,
16290         /**
16291          * @event click
16292          * Fires when a template node is clicked.
16293          * @param {Roo.View} this
16294          * @param {Number} index The index of the target node
16295          * @param {HTMLElement} node The target node
16296          * @param {Roo.EventObject} e The raw event object
16297          */
16298             "click" : true,
16299         /**
16300          * @event dblclick
16301          * Fires when a template node is double clicked.
16302          * @param {Roo.View} this
16303          * @param {Number} index The index of the target node
16304          * @param {HTMLElement} node The target node
16305          * @param {Roo.EventObject} e The raw event object
16306          */
16307             "dblclick" : true,
16308         /**
16309          * @event contextmenu
16310          * Fires when a template node is right clicked.
16311          * @param {Roo.View} this
16312          * @param {Number} index The index of the target node
16313          * @param {HTMLElement} node The target node
16314          * @param {Roo.EventObject} e The raw event object
16315          */
16316             "contextmenu" : true,
16317         /**
16318          * @event selectionchange
16319          * Fires when the selected nodes change.
16320          * @param {Roo.View} this
16321          * @param {Array} selections Array of the selected nodes
16322          */
16323             "selectionchange" : true,
16324     
16325         /**
16326          * @event beforeselect
16327          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16328          * @param {Roo.View} this
16329          * @param {HTMLElement} node The node to be selected
16330          * @param {Array} selections Array of currently selected nodes
16331          */
16332             "beforeselect" : true,
16333         /**
16334          * @event preparedata
16335          * Fires on every row to render, to allow you to change the data.
16336          * @param {Roo.View} this
16337          * @param {Object} data to be rendered (change this)
16338          */
16339           "preparedata" : true
16340           
16341           
16342         });
16343
16344
16345
16346     this.el.on({
16347         "click": this.onClick,
16348         "dblclick": this.onDblClick,
16349         "contextmenu": this.onContextMenu,
16350         scope:this
16351     });
16352
16353     this.selections = [];
16354     this.nodes = [];
16355     this.cmp = new Roo.CompositeElementLite([]);
16356     if(this.store){
16357         this.store = Roo.factory(this.store, Roo.data);
16358         this.setStore(this.store, true);
16359     }
16360     
16361     if ( this.footer && this.footer.xtype) {
16362            
16363          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16364         
16365         this.footer.dataSource = this.store;
16366         this.footer.container = fctr;
16367         this.footer = Roo.factory(this.footer, Roo);
16368         fctr.insertFirst(this.el);
16369         
16370         // this is a bit insane - as the paging toolbar seems to detach the el..
16371 //        dom.parentNode.parentNode.parentNode
16372          // they get detached?
16373     }
16374     
16375     
16376     Roo.View.superclass.constructor.call(this);
16377     
16378     
16379 };
16380
16381 Roo.extend(Roo.View, Roo.util.Observable, {
16382     
16383      /**
16384      * @cfg {Roo.data.Store} store Data store to load data from.
16385      */
16386     store : false,
16387     
16388     /**
16389      * @cfg {String|Roo.Element} el The container element.
16390      */
16391     el : '',
16392     
16393     /**
16394      * @cfg {String|Roo.Template} tpl The template used by this View 
16395      */
16396     tpl : false,
16397     /**
16398      * @cfg {String} dataName the named area of the template to use as the data area
16399      *                          Works with domtemplates roo-name="name"
16400      */
16401     dataName: false,
16402     /**
16403      * @cfg {String} selectedClass The css class to add to selected nodes
16404      */
16405     selectedClass : "x-view-selected",
16406      /**
16407      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16408      */
16409     emptyText : "",
16410     
16411     /**
16412      * @cfg {String} text to display on mask (default Loading)
16413      */
16414     mask : false,
16415     /**
16416      * @cfg {Boolean} multiSelect Allow multiple selection
16417      */
16418     multiSelect : false,
16419     /**
16420      * @cfg {Boolean} singleSelect Allow single selection
16421      */
16422     singleSelect:  false,
16423     
16424     /**
16425      * @cfg {Boolean} toggleSelect - selecting 
16426      */
16427     toggleSelect : false,
16428     
16429     /**
16430      * @cfg {Boolean} tickable - selecting 
16431      */
16432     tickable : false,
16433     
16434     /**
16435      * Returns the element this view is bound to.
16436      * @return {Roo.Element}
16437      */
16438     getEl : function(){
16439         return this.wrapEl;
16440     },
16441     
16442     
16443
16444     /**
16445      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16446      */
16447     refresh : function(){
16448         //Roo.log('refresh');
16449         var t = this.tpl;
16450         
16451         // if we are using something like 'domtemplate', then
16452         // the what gets used is:
16453         // t.applySubtemplate(NAME, data, wrapping data..)
16454         // the outer template then get' applied with
16455         //     the store 'extra data'
16456         // and the body get's added to the
16457         //      roo-name="data" node?
16458         //      <span class='roo-tpl-{name}'></span> ?????
16459         
16460         
16461         
16462         this.clearSelections();
16463         this.el.update("");
16464         var html = [];
16465         var records = this.store.getRange();
16466         if(records.length < 1) {
16467             
16468             // is this valid??  = should it render a template??
16469             
16470             this.el.update(this.emptyText);
16471             return;
16472         }
16473         var el = this.el;
16474         if (this.dataName) {
16475             this.el.update(t.apply(this.store.meta)); //????
16476             el = this.el.child('.roo-tpl-' + this.dataName);
16477         }
16478         
16479         for(var i = 0, len = records.length; i < len; i++){
16480             var data = this.prepareData(records[i].data, i, records[i]);
16481             this.fireEvent("preparedata", this, data, i, records[i]);
16482             
16483             var d = Roo.apply({}, data);
16484             
16485             if(this.tickable){
16486                 Roo.apply(d, {'roo-id' : Roo.id()});
16487                 
16488                 var _this = this;
16489             
16490                 Roo.each(this.parent.item, function(item){
16491                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16492                         return;
16493                     }
16494                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16495                 });
16496             }
16497             
16498             html[html.length] = Roo.util.Format.trim(
16499                 this.dataName ?
16500                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16501                     t.apply(d)
16502             );
16503         }
16504         
16505         
16506         
16507         el.update(html.join(""));
16508         this.nodes = el.dom.childNodes;
16509         this.updateIndexes(0);
16510     },
16511     
16512
16513     /**
16514      * Function to override to reformat the data that is sent to
16515      * the template for each node.
16516      * DEPRICATED - use the preparedata event handler.
16517      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16518      * a JSON object for an UpdateManager bound view).
16519      */
16520     prepareData : function(data, index, record)
16521     {
16522         this.fireEvent("preparedata", this, data, index, record);
16523         return data;
16524     },
16525
16526     onUpdate : function(ds, record){
16527         // Roo.log('on update');   
16528         this.clearSelections();
16529         var index = this.store.indexOf(record);
16530         var n = this.nodes[index];
16531         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16532         n.parentNode.removeChild(n);
16533         this.updateIndexes(index, index);
16534     },
16535
16536     
16537     
16538 // --------- FIXME     
16539     onAdd : function(ds, records, index)
16540     {
16541         //Roo.log(['on Add', ds, records, index] );        
16542         this.clearSelections();
16543         if(this.nodes.length == 0){
16544             this.refresh();
16545             return;
16546         }
16547         var n = this.nodes[index];
16548         for(var i = 0, len = records.length; i < len; i++){
16549             var d = this.prepareData(records[i].data, i, records[i]);
16550             if(n){
16551                 this.tpl.insertBefore(n, d);
16552             }else{
16553                 
16554                 this.tpl.append(this.el, d);
16555             }
16556         }
16557         this.updateIndexes(index);
16558     },
16559
16560     onRemove : function(ds, record, index){
16561        // Roo.log('onRemove');
16562         this.clearSelections();
16563         var el = this.dataName  ?
16564             this.el.child('.roo-tpl-' + this.dataName) :
16565             this.el; 
16566         
16567         el.dom.removeChild(this.nodes[index]);
16568         this.updateIndexes(index);
16569     },
16570
16571     /**
16572      * Refresh an individual node.
16573      * @param {Number} index
16574      */
16575     refreshNode : function(index){
16576         this.onUpdate(this.store, this.store.getAt(index));
16577     },
16578
16579     updateIndexes : function(startIndex, endIndex){
16580         var ns = this.nodes;
16581         startIndex = startIndex || 0;
16582         endIndex = endIndex || ns.length - 1;
16583         for(var i = startIndex; i <= endIndex; i++){
16584             ns[i].nodeIndex = i;
16585         }
16586     },
16587
16588     /**
16589      * Changes the data store this view uses and refresh the view.
16590      * @param {Store} store
16591      */
16592     setStore : function(store, initial){
16593         if(!initial && this.store){
16594             this.store.un("datachanged", this.refresh);
16595             this.store.un("add", this.onAdd);
16596             this.store.un("remove", this.onRemove);
16597             this.store.un("update", this.onUpdate);
16598             this.store.un("clear", this.refresh);
16599             this.store.un("beforeload", this.onBeforeLoad);
16600             this.store.un("load", this.onLoad);
16601             this.store.un("loadexception", this.onLoad);
16602         }
16603         if(store){
16604           
16605             store.on("datachanged", this.refresh, this);
16606             store.on("add", this.onAdd, this);
16607             store.on("remove", this.onRemove, this);
16608             store.on("update", this.onUpdate, this);
16609             store.on("clear", this.refresh, this);
16610             store.on("beforeload", this.onBeforeLoad, this);
16611             store.on("load", this.onLoad, this);
16612             store.on("loadexception", this.onLoad, this);
16613         }
16614         
16615         if(store){
16616             this.refresh();
16617         }
16618     },
16619     /**
16620      * onbeforeLoad - masks the loading area.
16621      *
16622      */
16623     onBeforeLoad : function(store,opts)
16624     {
16625          //Roo.log('onBeforeLoad');   
16626         if (!opts.add) {
16627             this.el.update("");
16628         }
16629         this.el.mask(this.mask ? this.mask : "Loading" ); 
16630     },
16631     onLoad : function ()
16632     {
16633         this.el.unmask();
16634     },
16635     
16636
16637     /**
16638      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16639      * @param {HTMLElement} node
16640      * @return {HTMLElement} The template node
16641      */
16642     findItemFromChild : function(node){
16643         var el = this.dataName  ?
16644             this.el.child('.roo-tpl-' + this.dataName,true) :
16645             this.el.dom; 
16646         
16647         if(!node || node.parentNode == el){
16648                     return node;
16649             }
16650             var p = node.parentNode;
16651             while(p && p != el){
16652             if(p.parentNode == el){
16653                 return p;
16654             }
16655             p = p.parentNode;
16656         }
16657             return null;
16658     },
16659
16660     /** @ignore */
16661     onClick : function(e){
16662         var item = this.findItemFromChild(e.getTarget());
16663         if(item){
16664             var index = this.indexOf(item);
16665             if(this.onItemClick(item, index, e) !== false){
16666                 this.fireEvent("click", this, index, item, e);
16667             }
16668         }else{
16669             this.clearSelections();
16670         }
16671     },
16672
16673     /** @ignore */
16674     onContextMenu : function(e){
16675         var item = this.findItemFromChild(e.getTarget());
16676         if(item){
16677             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16678         }
16679     },
16680
16681     /** @ignore */
16682     onDblClick : function(e){
16683         var item = this.findItemFromChild(e.getTarget());
16684         if(item){
16685             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16686         }
16687     },
16688
16689     onItemClick : function(item, index, e)
16690     {
16691         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16692             return false;
16693         }
16694         if (this.toggleSelect) {
16695             var m = this.isSelected(item) ? 'unselect' : 'select';
16696             //Roo.log(m);
16697             var _t = this;
16698             _t[m](item, true, false);
16699             return true;
16700         }
16701         if(this.multiSelect || this.singleSelect){
16702             if(this.multiSelect && e.shiftKey && this.lastSelection){
16703                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16704             }else{
16705                 this.select(item, this.multiSelect && e.ctrlKey);
16706                 this.lastSelection = item;
16707             }
16708             
16709             if(!this.tickable){
16710                 e.preventDefault();
16711             }
16712             
16713         }
16714         return true;
16715     },
16716
16717     /**
16718      * Get the number of selected nodes.
16719      * @return {Number}
16720      */
16721     getSelectionCount : function(){
16722         return this.selections.length;
16723     },
16724
16725     /**
16726      * Get the currently selected nodes.
16727      * @return {Array} An array of HTMLElements
16728      */
16729     getSelectedNodes : function(){
16730         return this.selections;
16731     },
16732
16733     /**
16734      * Get the indexes of the selected nodes.
16735      * @return {Array}
16736      */
16737     getSelectedIndexes : function(){
16738         var indexes = [], s = this.selections;
16739         for(var i = 0, len = s.length; i < len; i++){
16740             indexes.push(s[i].nodeIndex);
16741         }
16742         return indexes;
16743     },
16744
16745     /**
16746      * Clear all selections
16747      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16748      */
16749     clearSelections : function(suppressEvent){
16750         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16751             this.cmp.elements = this.selections;
16752             this.cmp.removeClass(this.selectedClass);
16753             this.selections = [];
16754             if(!suppressEvent){
16755                 this.fireEvent("selectionchange", this, this.selections);
16756             }
16757         }
16758     },
16759
16760     /**
16761      * Returns true if the passed node is selected
16762      * @param {HTMLElement/Number} node The node or node index
16763      * @return {Boolean}
16764      */
16765     isSelected : function(node){
16766         var s = this.selections;
16767         if(s.length < 1){
16768             return false;
16769         }
16770         node = this.getNode(node);
16771         return s.indexOf(node) !== -1;
16772     },
16773
16774     /**
16775      * Selects nodes.
16776      * @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
16777      * @param {Boolean} keepExisting (optional) true to keep existing selections
16778      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16779      */
16780     select : function(nodeInfo, keepExisting, suppressEvent){
16781         if(nodeInfo instanceof Array){
16782             if(!keepExisting){
16783                 this.clearSelections(true);
16784             }
16785             for(var i = 0, len = nodeInfo.length; i < len; i++){
16786                 this.select(nodeInfo[i], true, true);
16787             }
16788             return;
16789         } 
16790         var node = this.getNode(nodeInfo);
16791         if(!node || this.isSelected(node)){
16792             return; // already selected.
16793         }
16794         if(!keepExisting){
16795             this.clearSelections(true);
16796         }
16797         
16798         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16799             Roo.fly(node).addClass(this.selectedClass);
16800             this.selections.push(node);
16801             if(!suppressEvent){
16802                 this.fireEvent("selectionchange", this, this.selections);
16803             }
16804         }
16805         
16806         
16807     },
16808       /**
16809      * Unselects nodes.
16810      * @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
16811      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16812      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16813      */
16814     unselect : function(nodeInfo, keepExisting, suppressEvent)
16815     {
16816         if(nodeInfo instanceof Array){
16817             Roo.each(this.selections, function(s) {
16818                 this.unselect(s, nodeInfo);
16819             }, this);
16820             return;
16821         }
16822         var node = this.getNode(nodeInfo);
16823         if(!node || !this.isSelected(node)){
16824             //Roo.log("not selected");
16825             return; // not selected.
16826         }
16827         // fireevent???
16828         var ns = [];
16829         Roo.each(this.selections, function(s) {
16830             if (s == node ) {
16831                 Roo.fly(node).removeClass(this.selectedClass);
16832
16833                 return;
16834             }
16835             ns.push(s);
16836         },this);
16837         
16838         this.selections= ns;
16839         this.fireEvent("selectionchange", this, this.selections);
16840     },
16841
16842     /**
16843      * Gets a template node.
16844      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16845      * @return {HTMLElement} The node or null if it wasn't found
16846      */
16847     getNode : function(nodeInfo){
16848         if(typeof nodeInfo == "string"){
16849             return document.getElementById(nodeInfo);
16850         }else if(typeof nodeInfo == "number"){
16851             return this.nodes[nodeInfo];
16852         }
16853         return nodeInfo;
16854     },
16855
16856     /**
16857      * Gets a range template nodes.
16858      * @param {Number} startIndex
16859      * @param {Number} endIndex
16860      * @return {Array} An array of nodes
16861      */
16862     getNodes : function(start, end){
16863         var ns = this.nodes;
16864         start = start || 0;
16865         end = typeof end == "undefined" ? ns.length - 1 : end;
16866         var nodes = [];
16867         if(start <= end){
16868             for(var i = start; i <= end; i++){
16869                 nodes.push(ns[i]);
16870             }
16871         } else{
16872             for(var i = start; i >= end; i--){
16873                 nodes.push(ns[i]);
16874             }
16875         }
16876         return nodes;
16877     },
16878
16879     /**
16880      * Finds the index of the passed node
16881      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16882      * @return {Number} The index of the node or -1
16883      */
16884     indexOf : function(node){
16885         node = this.getNode(node);
16886         if(typeof node.nodeIndex == "number"){
16887             return node.nodeIndex;
16888         }
16889         var ns = this.nodes;
16890         for(var i = 0, len = ns.length; i < len; i++){
16891             if(ns[i] == node){
16892                 return i;
16893             }
16894         }
16895         return -1;
16896     }
16897 });
16898 /*
16899  * - LGPL
16900  *
16901  * based on jquery fullcalendar
16902  * 
16903  */
16904
16905 Roo.bootstrap = Roo.bootstrap || {};
16906 /**
16907  * @class Roo.bootstrap.Calendar
16908  * @extends Roo.bootstrap.Component
16909  * Bootstrap Calendar class
16910  * @cfg {Boolean} loadMask (true|false) default false
16911  * @cfg {Object} header generate the user specific header of the calendar, default false
16912
16913  * @constructor
16914  * Create a new Container
16915  * @param {Object} config The config object
16916  */
16917
16918
16919
16920 Roo.bootstrap.Calendar = function(config){
16921     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16922      this.addEvents({
16923         /**
16924              * @event select
16925              * Fires when a date is selected
16926              * @param {DatePicker} this
16927              * @param {Date} date The selected date
16928              */
16929         'select': true,
16930         /**
16931              * @event monthchange
16932              * Fires when the displayed month changes 
16933              * @param {DatePicker} this
16934              * @param {Date} date The selected month
16935              */
16936         'monthchange': true,
16937         /**
16938              * @event evententer
16939              * Fires when mouse over an event
16940              * @param {Calendar} this
16941              * @param {event} Event
16942              */
16943         'evententer': true,
16944         /**
16945              * @event eventleave
16946              * Fires when the mouse leaves an
16947              * @param {Calendar} this
16948              * @param {event}
16949              */
16950         'eventleave': true,
16951         /**
16952              * @event eventclick
16953              * Fires when the mouse click an
16954              * @param {Calendar} this
16955              * @param {event}
16956              */
16957         'eventclick': true
16958         
16959     });
16960
16961 };
16962
16963 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16964     
16965      /**
16966      * @cfg {Number} startDay
16967      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16968      */
16969     startDay : 0,
16970     
16971     loadMask : false,
16972     
16973     header : false,
16974       
16975     getAutoCreate : function(){
16976         
16977         
16978         var fc_button = function(name, corner, style, content ) {
16979             return Roo.apply({},{
16980                 tag : 'span',
16981                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16982                          (corner.length ?
16983                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16984                             ''
16985                         ),
16986                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16987                 unselectable: 'on'
16988             });
16989         };
16990         
16991         var header = {};
16992         
16993         if(!this.header){
16994             header = {
16995                 tag : 'table',
16996                 cls : 'fc-header',
16997                 style : 'width:100%',
16998                 cn : [
16999                     {
17000                         tag: 'tr',
17001                         cn : [
17002                             {
17003                                 tag : 'td',
17004                                 cls : 'fc-header-left',
17005                                 cn : [
17006                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
17007                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
17008                                     { tag: 'span', cls: 'fc-header-space' },
17009                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
17010
17011
17012                                 ]
17013                             },
17014
17015                             {
17016                                 tag : 'td',
17017                                 cls : 'fc-header-center',
17018                                 cn : [
17019                                     {
17020                                         tag: 'span',
17021                                         cls: 'fc-header-title',
17022                                         cn : {
17023                                             tag: 'H2',
17024                                             html : 'month / year'
17025                                         }
17026                                     }
17027
17028                                 ]
17029                             },
17030                             {
17031                                 tag : 'td',
17032                                 cls : 'fc-header-right',
17033                                 cn : [
17034                               /*      fc_button('month', 'left', '', 'month' ),
17035                                     fc_button('week', '', '', 'week' ),
17036                                     fc_button('day', 'right', '', 'day' )
17037                                 */    
17038
17039                                 ]
17040                             }
17041
17042                         ]
17043                     }
17044                 ]
17045             };
17046         }
17047         
17048         header = this.header;
17049         
17050        
17051         var cal_heads = function() {
17052             var ret = [];
17053             // fixme - handle this.
17054             
17055             for (var i =0; i < Date.dayNames.length; i++) {
17056                 var d = Date.dayNames[i];
17057                 ret.push({
17058                     tag: 'th',
17059                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17060                     html : d.substring(0,3)
17061                 });
17062                 
17063             }
17064             ret[0].cls += ' fc-first';
17065             ret[6].cls += ' fc-last';
17066             return ret;
17067         };
17068         var cal_cell = function(n) {
17069             return  {
17070                 tag: 'td',
17071                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17072                 cn : [
17073                     {
17074                         cn : [
17075                             {
17076                                 cls: 'fc-day-number',
17077                                 html: 'D'
17078                             },
17079                             {
17080                                 cls: 'fc-day-content',
17081                              
17082                                 cn : [
17083                                      {
17084                                         style: 'position: relative;' // height: 17px;
17085                                     }
17086                                 ]
17087                             }
17088                             
17089                             
17090                         ]
17091                     }
17092                 ]
17093                 
17094             }
17095         };
17096         var cal_rows = function() {
17097             
17098             var ret = [];
17099             for (var r = 0; r < 6; r++) {
17100                 var row= {
17101                     tag : 'tr',
17102                     cls : 'fc-week',
17103                     cn : []
17104                 };
17105                 
17106                 for (var i =0; i < Date.dayNames.length; i++) {
17107                     var d = Date.dayNames[i];
17108                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17109
17110                 }
17111                 row.cn[0].cls+=' fc-first';
17112                 row.cn[0].cn[0].style = 'min-height:90px';
17113                 row.cn[6].cls+=' fc-last';
17114                 ret.push(row);
17115                 
17116             }
17117             ret[0].cls += ' fc-first';
17118             ret[4].cls += ' fc-prev-last';
17119             ret[5].cls += ' fc-last';
17120             return ret;
17121             
17122         };
17123         
17124         var cal_table = {
17125             tag: 'table',
17126             cls: 'fc-border-separate',
17127             style : 'width:100%',
17128             cellspacing  : 0,
17129             cn : [
17130                 { 
17131                     tag: 'thead',
17132                     cn : [
17133                         { 
17134                             tag: 'tr',
17135                             cls : 'fc-first fc-last',
17136                             cn : cal_heads()
17137                         }
17138                     ]
17139                 },
17140                 { 
17141                     tag: 'tbody',
17142                     cn : cal_rows()
17143                 }
17144                   
17145             ]
17146         };
17147          
17148          var cfg = {
17149             cls : 'fc fc-ltr',
17150             cn : [
17151                 header,
17152                 {
17153                     cls : 'fc-content',
17154                     style : "position: relative;",
17155                     cn : [
17156                         {
17157                             cls : 'fc-view fc-view-month fc-grid',
17158                             style : 'position: relative',
17159                             unselectable : 'on',
17160                             cn : [
17161                                 {
17162                                     cls : 'fc-event-container',
17163                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17164                                 },
17165                                 cal_table
17166                             ]
17167                         }
17168                     ]
17169     
17170                 }
17171            ] 
17172             
17173         };
17174         
17175          
17176         
17177         return cfg;
17178     },
17179     
17180     
17181     initEvents : function()
17182     {
17183         if(!this.store){
17184             throw "can not find store for calendar";
17185         }
17186         
17187         var mark = {
17188             tag: "div",
17189             cls:"x-dlg-mask",
17190             style: "text-align:center",
17191             cn: [
17192                 {
17193                     tag: "div",
17194                     style: "background-color:white;width:50%;margin:250 auto",
17195                     cn: [
17196                         {
17197                             tag: "img",
17198                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17199                         },
17200                         {
17201                             tag: "span",
17202                             html: "Loading"
17203                         }
17204                         
17205                     ]
17206                 }
17207             ]
17208         };
17209         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17210         
17211         var size = this.el.select('.fc-content', true).first().getSize();
17212         this.maskEl.setSize(size.width, size.height);
17213         this.maskEl.enableDisplayMode("block");
17214         if(!this.loadMask){
17215             this.maskEl.hide();
17216         }
17217         
17218         this.store = Roo.factory(this.store, Roo.data);
17219         this.store.on('load', this.onLoad, this);
17220         this.store.on('beforeload', this.onBeforeLoad, this);
17221         
17222         this.resize();
17223         
17224         this.cells = this.el.select('.fc-day',true);
17225         //Roo.log(this.cells);
17226         this.textNodes = this.el.query('.fc-day-number');
17227         this.cells.addClassOnOver('fc-state-hover');
17228         
17229         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17230         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17231         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17232         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17233         
17234         this.on('monthchange', this.onMonthChange, this);
17235         
17236         this.update(new Date().clearTime());
17237     },
17238     
17239     resize : function() {
17240         var sz  = this.el.getSize();
17241         
17242         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17243         this.el.select('.fc-day-content div',true).setHeight(34);
17244     },
17245     
17246     
17247     // private
17248     showPrevMonth : function(e){
17249         this.update(this.activeDate.add("mo", -1));
17250     },
17251     showToday : function(e){
17252         this.update(new Date().clearTime());
17253     },
17254     // private
17255     showNextMonth : function(e){
17256         this.update(this.activeDate.add("mo", 1));
17257     },
17258
17259     // private
17260     showPrevYear : function(){
17261         this.update(this.activeDate.add("y", -1));
17262     },
17263
17264     // private
17265     showNextYear : function(){
17266         this.update(this.activeDate.add("y", 1));
17267     },
17268
17269     
17270    // private
17271     update : function(date)
17272     {
17273         var vd = this.activeDate;
17274         this.activeDate = date;
17275 //        if(vd && this.el){
17276 //            var t = date.getTime();
17277 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17278 //                Roo.log('using add remove');
17279 //                
17280 //                this.fireEvent('monthchange', this, date);
17281 //                
17282 //                this.cells.removeClass("fc-state-highlight");
17283 //                this.cells.each(function(c){
17284 //                   if(c.dateValue == t){
17285 //                       c.addClass("fc-state-highlight");
17286 //                       setTimeout(function(){
17287 //                            try{c.dom.firstChild.focus();}catch(e){}
17288 //                       }, 50);
17289 //                       return false;
17290 //                   }
17291 //                   return true;
17292 //                });
17293 //                return;
17294 //            }
17295 //        }
17296         
17297         var days = date.getDaysInMonth();
17298         
17299         var firstOfMonth = date.getFirstDateOfMonth();
17300         var startingPos = firstOfMonth.getDay()-this.startDay;
17301         
17302         if(startingPos < this.startDay){
17303             startingPos += 7;
17304         }
17305         
17306         var pm = date.add(Date.MONTH, -1);
17307         var prevStart = pm.getDaysInMonth()-startingPos;
17308 //        
17309         this.cells = this.el.select('.fc-day',true);
17310         this.textNodes = this.el.query('.fc-day-number');
17311         this.cells.addClassOnOver('fc-state-hover');
17312         
17313         var cells = this.cells.elements;
17314         var textEls = this.textNodes;
17315         
17316         Roo.each(cells, function(cell){
17317             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17318         });
17319         
17320         days += startingPos;
17321
17322         // convert everything to numbers so it's fast
17323         var day = 86400000;
17324         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17325         //Roo.log(d);
17326         //Roo.log(pm);
17327         //Roo.log(prevStart);
17328         
17329         var today = new Date().clearTime().getTime();
17330         var sel = date.clearTime().getTime();
17331         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17332         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17333         var ddMatch = this.disabledDatesRE;
17334         var ddText = this.disabledDatesText;
17335         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17336         var ddaysText = this.disabledDaysText;
17337         var format = this.format;
17338         
17339         var setCellClass = function(cal, cell){
17340             cell.row = 0;
17341             cell.events = [];
17342             cell.more = [];
17343             //Roo.log('set Cell Class');
17344             cell.title = "";
17345             var t = d.getTime();
17346             
17347             //Roo.log(d);
17348             
17349             cell.dateValue = t;
17350             if(t == today){
17351                 cell.className += " fc-today";
17352                 cell.className += " fc-state-highlight";
17353                 cell.title = cal.todayText;
17354             }
17355             if(t == sel){
17356                 // disable highlight in other month..
17357                 //cell.className += " fc-state-highlight";
17358                 
17359             }
17360             // disabling
17361             if(t < min) {
17362                 cell.className = " fc-state-disabled";
17363                 cell.title = cal.minText;
17364                 return;
17365             }
17366             if(t > max) {
17367                 cell.className = " fc-state-disabled";
17368                 cell.title = cal.maxText;
17369                 return;
17370             }
17371             if(ddays){
17372                 if(ddays.indexOf(d.getDay()) != -1){
17373                     cell.title = ddaysText;
17374                     cell.className = " fc-state-disabled";
17375                 }
17376             }
17377             if(ddMatch && format){
17378                 var fvalue = d.dateFormat(format);
17379                 if(ddMatch.test(fvalue)){
17380                     cell.title = ddText.replace("%0", fvalue);
17381                     cell.className = " fc-state-disabled";
17382                 }
17383             }
17384             
17385             if (!cell.initialClassName) {
17386                 cell.initialClassName = cell.dom.className;
17387             }
17388             
17389             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17390         };
17391
17392         var i = 0;
17393         
17394         for(; i < startingPos; i++) {
17395             textEls[i].innerHTML = (++prevStart);
17396             d.setDate(d.getDate()+1);
17397             
17398             cells[i].className = "fc-past fc-other-month";
17399             setCellClass(this, cells[i]);
17400         }
17401         
17402         var intDay = 0;
17403         
17404         for(; i < days; i++){
17405             intDay = i - startingPos + 1;
17406             textEls[i].innerHTML = (intDay);
17407             d.setDate(d.getDate()+1);
17408             
17409             cells[i].className = ''; // "x-date-active";
17410             setCellClass(this, cells[i]);
17411         }
17412         var extraDays = 0;
17413         
17414         for(; i < 42; i++) {
17415             textEls[i].innerHTML = (++extraDays);
17416             d.setDate(d.getDate()+1);
17417             
17418             cells[i].className = "fc-future fc-other-month";
17419             setCellClass(this, cells[i]);
17420         }
17421         
17422         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17423         
17424         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17425         
17426         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17427         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17428         
17429         if(totalRows != 6){
17430             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17431             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17432         }
17433         
17434         this.fireEvent('monthchange', this, date);
17435         
17436         
17437         /*
17438         if(!this.internalRender){
17439             var main = this.el.dom.firstChild;
17440             var w = main.offsetWidth;
17441             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17442             Roo.fly(main).setWidth(w);
17443             this.internalRender = true;
17444             // opera does not respect the auto grow header center column
17445             // then, after it gets a width opera refuses to recalculate
17446             // without a second pass
17447             if(Roo.isOpera && !this.secondPass){
17448                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17449                 this.secondPass = true;
17450                 this.update.defer(10, this, [date]);
17451             }
17452         }
17453         */
17454         
17455     },
17456     
17457     findCell : function(dt) {
17458         dt = dt.clearTime().getTime();
17459         var ret = false;
17460         this.cells.each(function(c){
17461             //Roo.log("check " +c.dateValue + '?=' + dt);
17462             if(c.dateValue == dt){
17463                 ret = c;
17464                 return false;
17465             }
17466             return true;
17467         });
17468         
17469         return ret;
17470     },
17471     
17472     findCells : function(ev) {
17473         var s = ev.start.clone().clearTime().getTime();
17474        // Roo.log(s);
17475         var e= ev.end.clone().clearTime().getTime();
17476        // Roo.log(e);
17477         var ret = [];
17478         this.cells.each(function(c){
17479              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17480             
17481             if(c.dateValue > e){
17482                 return ;
17483             }
17484             if(c.dateValue < s){
17485                 return ;
17486             }
17487             ret.push(c);
17488         });
17489         
17490         return ret;    
17491     },
17492     
17493 //    findBestRow: function(cells)
17494 //    {
17495 //        var ret = 0;
17496 //        
17497 //        for (var i =0 ; i < cells.length;i++) {
17498 //            ret  = Math.max(cells[i].rows || 0,ret);
17499 //        }
17500 //        return ret;
17501 //        
17502 //    },
17503     
17504     
17505     addItem : function(ev)
17506     {
17507         // look for vertical location slot in
17508         var cells = this.findCells(ev);
17509         
17510 //        ev.row = this.findBestRow(cells);
17511         
17512         // work out the location.
17513         
17514         var crow = false;
17515         var rows = [];
17516         for(var i =0; i < cells.length; i++) {
17517             
17518             cells[i].row = cells[0].row;
17519             
17520             if(i == 0){
17521                 cells[i].row = cells[i].row + 1;
17522             }
17523             
17524             if (!crow) {
17525                 crow = {
17526                     start : cells[i],
17527                     end :  cells[i]
17528                 };
17529                 continue;
17530             }
17531             if (crow.start.getY() == cells[i].getY()) {
17532                 // on same row.
17533                 crow.end = cells[i];
17534                 continue;
17535             }
17536             // different row.
17537             rows.push(crow);
17538             crow = {
17539                 start: cells[i],
17540                 end : cells[i]
17541             };
17542             
17543         }
17544         
17545         rows.push(crow);
17546         ev.els = [];
17547         ev.rows = rows;
17548         ev.cells = cells;
17549         
17550         cells[0].events.push(ev);
17551         
17552         this.calevents.push(ev);
17553     },
17554     
17555     clearEvents: function() {
17556         
17557         if(!this.calevents){
17558             return;
17559         }
17560         
17561         Roo.each(this.cells.elements, function(c){
17562             c.row = 0;
17563             c.events = [];
17564             c.more = [];
17565         });
17566         
17567         Roo.each(this.calevents, function(e) {
17568             Roo.each(e.els, function(el) {
17569                 el.un('mouseenter' ,this.onEventEnter, this);
17570                 el.un('mouseleave' ,this.onEventLeave, this);
17571                 el.remove();
17572             },this);
17573         },this);
17574         
17575         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17576             e.remove();
17577         });
17578         
17579     },
17580     
17581     renderEvents: function()
17582     {   
17583         var _this = this;
17584         
17585         this.cells.each(function(c) {
17586             
17587             if(c.row < 5){
17588                 return;
17589             }
17590             
17591             var ev = c.events;
17592             
17593             var r = 4;
17594             if(c.row != c.events.length){
17595                 r = 4 - (4 - (c.row - c.events.length));
17596             }
17597             
17598             c.events = ev.slice(0, r);
17599             c.more = ev.slice(r);
17600             
17601             if(c.more.length && c.more.length == 1){
17602                 c.events.push(c.more.pop());
17603             }
17604             
17605             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17606             
17607         });
17608             
17609         this.cells.each(function(c) {
17610             
17611             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17612             
17613             
17614             for (var e = 0; e < c.events.length; e++){
17615                 var ev = c.events[e];
17616                 var rows = ev.rows;
17617                 
17618                 for(var i = 0; i < rows.length; i++) {
17619                 
17620                     // how many rows should it span..
17621
17622                     var  cfg = {
17623                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17624                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17625
17626                         unselectable : "on",
17627                         cn : [
17628                             {
17629                                 cls: 'fc-event-inner',
17630                                 cn : [
17631     //                                {
17632     //                                  tag:'span',
17633     //                                  cls: 'fc-event-time',
17634     //                                  html : cells.length > 1 ? '' : ev.time
17635     //                                },
17636                                     {
17637                                       tag:'span',
17638                                       cls: 'fc-event-title',
17639                                       html : String.format('{0}', ev.title)
17640                                     }
17641
17642
17643                                 ]
17644                             },
17645                             {
17646                                 cls: 'ui-resizable-handle ui-resizable-e',
17647                                 html : '&nbsp;&nbsp;&nbsp'
17648                             }
17649
17650                         ]
17651                     };
17652
17653                     if (i == 0) {
17654                         cfg.cls += ' fc-event-start';
17655                     }
17656                     if ((i+1) == rows.length) {
17657                         cfg.cls += ' fc-event-end';
17658                     }
17659
17660                     var ctr = _this.el.select('.fc-event-container',true).first();
17661                     var cg = ctr.createChild(cfg);
17662
17663                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17664                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17665
17666                     var r = (c.more.length) ? 1 : 0;
17667                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17668                     cg.setWidth(ebox.right - sbox.x -2);
17669
17670                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17671                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17672                     cg.on('click', _this.onEventClick, _this, ev);
17673
17674                     ev.els.push(cg);
17675                     
17676                 }
17677                 
17678             }
17679             
17680             
17681             if(c.more.length){
17682                 var  cfg = {
17683                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17684                     style : 'position: absolute',
17685                     unselectable : "on",
17686                     cn : [
17687                         {
17688                             cls: 'fc-event-inner',
17689                             cn : [
17690                                 {
17691                                   tag:'span',
17692                                   cls: 'fc-event-title',
17693                                   html : 'More'
17694                                 }
17695
17696
17697                             ]
17698                         },
17699                         {
17700                             cls: 'ui-resizable-handle ui-resizable-e',
17701                             html : '&nbsp;&nbsp;&nbsp'
17702                         }
17703
17704                     ]
17705                 };
17706
17707                 var ctr = _this.el.select('.fc-event-container',true).first();
17708                 var cg = ctr.createChild(cfg);
17709
17710                 var sbox = c.select('.fc-day-content',true).first().getBox();
17711                 var ebox = c.select('.fc-day-content',true).first().getBox();
17712                 //Roo.log(cg);
17713                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17714                 cg.setWidth(ebox.right - sbox.x -2);
17715
17716                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17717                 
17718             }
17719             
17720         });
17721         
17722         
17723         
17724     },
17725     
17726     onEventEnter: function (e, el,event,d) {
17727         this.fireEvent('evententer', this, el, event);
17728     },
17729     
17730     onEventLeave: function (e, el,event,d) {
17731         this.fireEvent('eventleave', this, el, event);
17732     },
17733     
17734     onEventClick: function (e, el,event,d) {
17735         this.fireEvent('eventclick', this, el, event);
17736     },
17737     
17738     onMonthChange: function () {
17739         this.store.load();
17740     },
17741     
17742     onMoreEventClick: function(e, el, more)
17743     {
17744         var _this = this;
17745         
17746         this.calpopover.placement = 'right';
17747         this.calpopover.setTitle('More');
17748         
17749         this.calpopover.setContent('');
17750         
17751         var ctr = this.calpopover.el.select('.popover-content', true).first();
17752         
17753         Roo.each(more, function(m){
17754             var cfg = {
17755                 cls : 'fc-event-hori fc-event-draggable',
17756                 html : m.title
17757             };
17758             var cg = ctr.createChild(cfg);
17759             
17760             cg.on('click', _this.onEventClick, _this, m);
17761         });
17762         
17763         this.calpopover.show(el);
17764         
17765         
17766     },
17767     
17768     onLoad: function () 
17769     {   
17770         this.calevents = [];
17771         var cal = this;
17772         
17773         if(this.store.getCount() > 0){
17774             this.store.data.each(function(d){
17775                cal.addItem({
17776                     id : d.data.id,
17777                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17778                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17779                     time : d.data.start_time,
17780                     title : d.data.title,
17781                     description : d.data.description,
17782                     venue : d.data.venue
17783                 });
17784             });
17785         }
17786         
17787         this.renderEvents();
17788         
17789         if(this.calevents.length && this.loadMask){
17790             this.maskEl.hide();
17791         }
17792     },
17793     
17794     onBeforeLoad: function()
17795     {
17796         this.clearEvents();
17797         if(this.loadMask){
17798             this.maskEl.show();
17799         }
17800     }
17801 });
17802
17803  
17804  /*
17805  * - LGPL
17806  *
17807  * element
17808  * 
17809  */
17810
17811 /**
17812  * @class Roo.bootstrap.Popover
17813  * @extends Roo.bootstrap.Component
17814  * Bootstrap Popover class
17815  * @cfg {String} html contents of the popover   (or false to use children..)
17816  * @cfg {String} title of popover (or false to hide)
17817  * @cfg {String} placement how it is placed
17818  * @cfg {String} trigger click || hover (or false to trigger manually)
17819  * @cfg {String} over what (parent or false to trigger manually.)
17820  * @cfg {Number} delay - delay before showing
17821  
17822  * @constructor
17823  * Create a new Popover
17824  * @param {Object} config The config object
17825  */
17826
17827 Roo.bootstrap.Popover = function(config){
17828     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17829     
17830     this.addEvents({
17831         // raw events
17832          /**
17833          * @event show
17834          * After the popover show
17835          * 
17836          * @param {Roo.bootstrap.Popover} this
17837          */
17838         "show" : true,
17839         /**
17840          * @event hide
17841          * After the popover hide
17842          * 
17843          * @param {Roo.bootstrap.Popover} this
17844          */
17845         "hide" : true
17846     });
17847 };
17848
17849 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17850     
17851     title: 'Fill in a title',
17852     html: false,
17853     
17854     placement : 'right',
17855     trigger : 'hover', // hover
17856     
17857     delay : 0,
17858     
17859     over: 'parent',
17860     
17861     can_build_overlaid : false,
17862     
17863     getChildContainer : function()
17864     {
17865         return this.el.select('.popover-content',true).first();
17866     },
17867     
17868     getAutoCreate : function(){
17869          
17870         var cfg = {
17871            cls : 'popover roo-dynamic',
17872            style: 'display:block',
17873            cn : [
17874                 {
17875                     cls : 'arrow'
17876                 },
17877                 {
17878                     cls : 'popover-inner',
17879                     cn : [
17880                         {
17881                             tag: 'h3',
17882                             cls: 'popover-title popover-header',
17883                             html : this.title
17884                         },
17885                         {
17886                             cls : 'popover-content popover-body',
17887                             html : this.html
17888                         }
17889                     ]
17890                     
17891                 }
17892            ]
17893         };
17894         
17895         return cfg;
17896     },
17897     setTitle: function(str)
17898     {
17899         this.title = str;
17900         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17901     },
17902     setContent: function(str)
17903     {
17904         this.html = str;
17905         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17906     },
17907     // as it get's added to the bottom of the page.
17908     onRender : function(ct, position)
17909     {
17910         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17911         if(!this.el){
17912             var cfg = Roo.apply({},  this.getAutoCreate());
17913             cfg.id = Roo.id();
17914             
17915             if (this.cls) {
17916                 cfg.cls += ' ' + this.cls;
17917             }
17918             if (this.style) {
17919                 cfg.style = this.style;
17920             }
17921             //Roo.log("adding to ");
17922             this.el = Roo.get(document.body).createChild(cfg, position);
17923 //            Roo.log(this.el);
17924         }
17925         this.initEvents();
17926     },
17927     
17928     initEvents : function()
17929     {
17930         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17931         this.el.enableDisplayMode('block');
17932         this.el.hide();
17933         if (this.over === false) {
17934             return; 
17935         }
17936         if (this.triggers === false) {
17937             return;
17938         }
17939         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17940         var triggers = this.trigger ? this.trigger.split(' ') : [];
17941         Roo.each(triggers, function(trigger) {
17942         
17943             if (trigger == 'click') {
17944                 on_el.on('click', this.toggle, this);
17945             } else if (trigger != 'manual') {
17946                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17947                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17948       
17949                 on_el.on(eventIn  ,this.enter, this);
17950                 on_el.on(eventOut, this.leave, this);
17951             }
17952         }, this);
17953         
17954     },
17955     
17956     
17957     // private
17958     timeout : null,
17959     hoverState : null,
17960     
17961     toggle : function () {
17962         this.hoverState == 'in' ? this.leave() : this.enter();
17963     },
17964     
17965     enter : function () {
17966         
17967         clearTimeout(this.timeout);
17968     
17969         this.hoverState = 'in';
17970     
17971         if (!this.delay || !this.delay.show) {
17972             this.show();
17973             return;
17974         }
17975         var _t = this;
17976         this.timeout = setTimeout(function () {
17977             if (_t.hoverState == 'in') {
17978                 _t.show();
17979             }
17980         }, this.delay.show)
17981     },
17982     
17983     leave : function() {
17984         clearTimeout(this.timeout);
17985     
17986         this.hoverState = 'out';
17987     
17988         if (!this.delay || !this.delay.hide) {
17989             this.hide();
17990             return;
17991         }
17992         var _t = this;
17993         this.timeout = setTimeout(function () {
17994             if (_t.hoverState == 'out') {
17995                 _t.hide();
17996             }
17997         }, this.delay.hide)
17998     },
17999     
18000     show : function (on_el)
18001     {
18002         if (!on_el) {
18003             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
18004         }
18005         
18006         // set content.
18007         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
18008         if (this.html !== false) {
18009             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
18010         }
18011         this.el.removeClass([
18012             'fade','top','bottom', 'left', 'right','in',
18013             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
18014         ]);
18015         if (!this.title.length) {
18016             this.el.select('.popover-title',true).hide();
18017         }
18018         
18019         var placement = typeof this.placement == 'function' ?
18020             this.placement.call(this, this.el, on_el) :
18021             this.placement;
18022             
18023         var autoToken = /\s?auto?\s?/i;
18024         var autoPlace = autoToken.test(placement);
18025         if (autoPlace) {
18026             placement = placement.replace(autoToken, '') || 'top';
18027         }
18028         
18029         //this.el.detach()
18030         //this.el.setXY([0,0]);
18031         this.el.show();
18032         this.el.dom.style.display='block';
18033         this.el.addClass(placement);
18034         
18035         //this.el.appendTo(on_el);
18036         
18037         var p = this.getPosition();
18038         var box = this.el.getBox();
18039         
18040         if (autoPlace) {
18041             // fixme..
18042         }
18043         var align = Roo.bootstrap.Popover.alignment[placement];
18044         
18045 //        Roo.log(align);
18046         this.el.alignTo(on_el, align[0],align[1]);
18047         //var arrow = this.el.select('.arrow',true).first();
18048         //arrow.set(align[2], 
18049         
18050         this.el.addClass('in');
18051         
18052         
18053         if (this.el.hasClass('fade')) {
18054             // fade it?
18055         }
18056         
18057         this.hoverState = 'in';
18058         
18059         this.fireEvent('show', this);
18060         
18061     },
18062     hide : function()
18063     {
18064         this.el.setXY([0,0]);
18065         this.el.removeClass('in');
18066         this.el.hide();
18067         this.hoverState = null;
18068         
18069         this.fireEvent('hide', this);
18070     }
18071     
18072 });
18073
18074 Roo.bootstrap.Popover.alignment = {
18075     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18076     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18077     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18078     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18079 };
18080
18081  /*
18082  * - LGPL
18083  *
18084  * Progress
18085  * 
18086  */
18087
18088 /**
18089  * @class Roo.bootstrap.Progress
18090  * @extends Roo.bootstrap.Component
18091  * Bootstrap Progress class
18092  * @cfg {Boolean} striped striped of the progress bar
18093  * @cfg {Boolean} active animated of the progress bar
18094  * 
18095  * 
18096  * @constructor
18097  * Create a new Progress
18098  * @param {Object} config The config object
18099  */
18100
18101 Roo.bootstrap.Progress = function(config){
18102     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18103 };
18104
18105 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18106     
18107     striped : false,
18108     active: false,
18109     
18110     getAutoCreate : function(){
18111         var cfg = {
18112             tag: 'div',
18113             cls: 'progress'
18114         };
18115         
18116         
18117         if(this.striped){
18118             cfg.cls += ' progress-striped';
18119         }
18120       
18121         if(this.active){
18122             cfg.cls += ' active';
18123         }
18124         
18125         
18126         return cfg;
18127     }
18128    
18129 });
18130
18131  
18132
18133  /*
18134  * - LGPL
18135  *
18136  * ProgressBar
18137  * 
18138  */
18139
18140 /**
18141  * @class Roo.bootstrap.ProgressBar
18142  * @extends Roo.bootstrap.Component
18143  * Bootstrap ProgressBar class
18144  * @cfg {Number} aria_valuenow aria-value now
18145  * @cfg {Number} aria_valuemin aria-value min
18146  * @cfg {Number} aria_valuemax aria-value max
18147  * @cfg {String} label label for the progress bar
18148  * @cfg {String} panel (success | info | warning | danger )
18149  * @cfg {String} role role of the progress bar
18150  * @cfg {String} sr_only text
18151  * 
18152  * 
18153  * @constructor
18154  * Create a new ProgressBar
18155  * @param {Object} config The config object
18156  */
18157
18158 Roo.bootstrap.ProgressBar = function(config){
18159     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18160 };
18161
18162 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18163     
18164     aria_valuenow : 0,
18165     aria_valuemin : 0,
18166     aria_valuemax : 100,
18167     label : false,
18168     panel : false,
18169     role : false,
18170     sr_only: false,
18171     
18172     getAutoCreate : function()
18173     {
18174         
18175         var cfg = {
18176             tag: 'div',
18177             cls: 'progress-bar',
18178             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18179         };
18180         
18181         if(this.sr_only){
18182             cfg.cn = {
18183                 tag: 'span',
18184                 cls: 'sr-only',
18185                 html: this.sr_only
18186             }
18187         }
18188         
18189         if(this.role){
18190             cfg.role = this.role;
18191         }
18192         
18193         if(this.aria_valuenow){
18194             cfg['aria-valuenow'] = this.aria_valuenow;
18195         }
18196         
18197         if(this.aria_valuemin){
18198             cfg['aria-valuemin'] = this.aria_valuemin;
18199         }
18200         
18201         if(this.aria_valuemax){
18202             cfg['aria-valuemax'] = this.aria_valuemax;
18203         }
18204         
18205         if(this.label && !this.sr_only){
18206             cfg.html = this.label;
18207         }
18208         
18209         if(this.panel){
18210             cfg.cls += ' progress-bar-' + this.panel;
18211         }
18212         
18213         return cfg;
18214     },
18215     
18216     update : function(aria_valuenow)
18217     {
18218         this.aria_valuenow = aria_valuenow;
18219         
18220         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18221     }
18222    
18223 });
18224
18225  
18226
18227  /*
18228  * - LGPL
18229  *
18230  * column
18231  * 
18232  */
18233
18234 /**
18235  * @class Roo.bootstrap.TabGroup
18236  * @extends Roo.bootstrap.Column
18237  * Bootstrap Column class
18238  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18239  * @cfg {Boolean} carousel true to make the group behave like a carousel
18240  * @cfg {Boolean} bullets show bullets for the panels
18241  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18242  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18243  * @cfg {Boolean} showarrow (true|false) show arrow default true
18244  * 
18245  * @constructor
18246  * Create a new TabGroup
18247  * @param {Object} config The config object
18248  */
18249
18250 Roo.bootstrap.TabGroup = function(config){
18251     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18252     if (!this.navId) {
18253         this.navId = Roo.id();
18254     }
18255     this.tabs = [];
18256     Roo.bootstrap.TabGroup.register(this);
18257     
18258 };
18259
18260 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18261     
18262     carousel : false,
18263     transition : false,
18264     bullets : 0,
18265     timer : 0,
18266     autoslide : false,
18267     slideFn : false,
18268     slideOnTouch : false,
18269     showarrow : true,
18270     
18271     getAutoCreate : function()
18272     {
18273         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18274         
18275         cfg.cls += ' tab-content';
18276         
18277         if (this.carousel) {
18278             cfg.cls += ' carousel slide';
18279             
18280             cfg.cn = [{
18281                cls : 'carousel-inner',
18282                cn : []
18283             }];
18284         
18285             if(this.bullets  && !Roo.isTouch){
18286                 
18287                 var bullets = {
18288                     cls : 'carousel-bullets',
18289                     cn : []
18290                 };
18291                
18292                 if(this.bullets_cls){
18293                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18294                 }
18295                 
18296                 bullets.cn.push({
18297                     cls : 'clear'
18298                 });
18299                 
18300                 cfg.cn[0].cn.push(bullets);
18301             }
18302             
18303             if(this.showarrow){
18304                 cfg.cn[0].cn.push({
18305                     tag : 'div',
18306                     class : 'carousel-arrow',
18307                     cn : [
18308                         {
18309                             tag : 'div',
18310                             class : 'carousel-prev',
18311                             cn : [
18312                                 {
18313                                     tag : 'i',
18314                                     class : 'fa fa-chevron-left'
18315                                 }
18316                             ]
18317                         },
18318                         {
18319                             tag : 'div',
18320                             class : 'carousel-next',
18321                             cn : [
18322                                 {
18323                                     tag : 'i',
18324                                     class : 'fa fa-chevron-right'
18325                                 }
18326                             ]
18327                         }
18328                     ]
18329                 });
18330             }
18331             
18332         }
18333         
18334         return cfg;
18335     },
18336     
18337     initEvents:  function()
18338     {
18339 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18340 //            this.el.on("touchstart", this.onTouchStart, this);
18341 //        }
18342         
18343         if(this.autoslide){
18344             var _this = this;
18345             
18346             this.slideFn = window.setInterval(function() {
18347                 _this.showPanelNext();
18348             }, this.timer);
18349         }
18350         
18351         if(this.showarrow){
18352             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18353             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18354         }
18355         
18356         
18357     },
18358     
18359 //    onTouchStart : function(e, el, o)
18360 //    {
18361 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18362 //            return;
18363 //        }
18364 //        
18365 //        this.showPanelNext();
18366 //    },
18367     
18368     
18369     getChildContainer : function()
18370     {
18371         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18372     },
18373     
18374     /**
18375     * register a Navigation item
18376     * @param {Roo.bootstrap.NavItem} the navitem to add
18377     */
18378     register : function(item)
18379     {
18380         this.tabs.push( item);
18381         item.navId = this.navId; // not really needed..
18382         this.addBullet();
18383     
18384     },
18385     
18386     getActivePanel : function()
18387     {
18388         var r = false;
18389         Roo.each(this.tabs, function(t) {
18390             if (t.active) {
18391                 r = t;
18392                 return false;
18393             }
18394             return null;
18395         });
18396         return r;
18397         
18398     },
18399     getPanelByName : function(n)
18400     {
18401         var r = false;
18402         Roo.each(this.tabs, function(t) {
18403             if (t.tabId == n) {
18404                 r = t;
18405                 return false;
18406             }
18407             return null;
18408         });
18409         return r;
18410     },
18411     indexOfPanel : function(p)
18412     {
18413         var r = false;
18414         Roo.each(this.tabs, function(t,i) {
18415             if (t.tabId == p.tabId) {
18416                 r = i;
18417                 return false;
18418             }
18419             return null;
18420         });
18421         return r;
18422     },
18423     /**
18424      * show a specific panel
18425      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18426      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18427      */
18428     showPanel : function (pan)
18429     {
18430         if(this.transition || typeof(pan) == 'undefined'){
18431             Roo.log("waiting for the transitionend");
18432             return false;
18433         }
18434         
18435         if (typeof(pan) == 'number') {
18436             pan = this.tabs[pan];
18437         }
18438         
18439         if (typeof(pan) == 'string') {
18440             pan = this.getPanelByName(pan);
18441         }
18442         
18443         var cur = this.getActivePanel();
18444         
18445         if(!pan || !cur){
18446             Roo.log('pan or acitve pan is undefined');
18447             return false;
18448         }
18449         
18450         if (pan.tabId == this.getActivePanel().tabId) {
18451             return true;
18452         }
18453         
18454         if (false === cur.fireEvent('beforedeactivate')) {
18455             return false;
18456         }
18457         
18458         if(this.bullets > 0 && !Roo.isTouch){
18459             this.setActiveBullet(this.indexOfPanel(pan));
18460         }
18461         
18462         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18463             
18464             //class="carousel-item carousel-item-next carousel-item-left"
18465             
18466             this.transition = true;
18467             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18468             var lr = dir == 'next' ? 'left' : 'right';
18469             pan.el.addClass(dir); // or prev
18470             pan.el.addClass('carousel-item-' + dir); // or prev
18471             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18472             cur.el.addClass(lr); // or right
18473             pan.el.addClass(lr);
18474             cur.el.addClass('carousel-item-' +lr); // or right
18475             pan.el.addClass('carousel-item-' +lr);
18476             
18477             
18478             var _this = this;
18479             cur.el.on('transitionend', function() {
18480                 Roo.log("trans end?");
18481                 
18482                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18483                 pan.setActive(true);
18484                 
18485                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18486                 cur.setActive(false);
18487                 
18488                 _this.transition = false;
18489                 
18490             }, this, { single:  true } );
18491             
18492             return true;
18493         }
18494         
18495         cur.setActive(false);
18496         pan.setActive(true);
18497         
18498         return true;
18499         
18500     },
18501     showPanelNext : function()
18502     {
18503         var i = this.indexOfPanel(this.getActivePanel());
18504         
18505         if (i >= this.tabs.length - 1 && !this.autoslide) {
18506             return;
18507         }
18508         
18509         if (i >= this.tabs.length - 1 && this.autoslide) {
18510             i = -1;
18511         }
18512         
18513         this.showPanel(this.tabs[i+1]);
18514     },
18515     
18516     showPanelPrev : function()
18517     {
18518         var i = this.indexOfPanel(this.getActivePanel());
18519         
18520         if (i  < 1 && !this.autoslide) {
18521             return;
18522         }
18523         
18524         if (i < 1 && this.autoslide) {
18525             i = this.tabs.length;
18526         }
18527         
18528         this.showPanel(this.tabs[i-1]);
18529     },
18530     
18531     
18532     addBullet: function()
18533     {
18534         if(!this.bullets || Roo.isTouch){
18535             return;
18536         }
18537         var ctr = this.el.select('.carousel-bullets',true).first();
18538         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18539         var bullet = ctr.createChild({
18540             cls : 'bullet bullet-' + i
18541         },ctr.dom.lastChild);
18542         
18543         
18544         var _this = this;
18545         
18546         bullet.on('click', (function(e, el, o, ii, t){
18547
18548             e.preventDefault();
18549
18550             this.showPanel(ii);
18551
18552             if(this.autoslide && this.slideFn){
18553                 clearInterval(this.slideFn);
18554                 this.slideFn = window.setInterval(function() {
18555                     _this.showPanelNext();
18556                 }, this.timer);
18557             }
18558
18559         }).createDelegate(this, [i, bullet], true));
18560                 
18561         
18562     },
18563      
18564     setActiveBullet : function(i)
18565     {
18566         if(Roo.isTouch){
18567             return;
18568         }
18569         
18570         Roo.each(this.el.select('.bullet', true).elements, function(el){
18571             el.removeClass('selected');
18572         });
18573
18574         var bullet = this.el.select('.bullet-' + i, true).first();
18575         
18576         if(!bullet){
18577             return;
18578         }
18579         
18580         bullet.addClass('selected');
18581     }
18582     
18583     
18584   
18585 });
18586
18587  
18588
18589  
18590  
18591 Roo.apply(Roo.bootstrap.TabGroup, {
18592     
18593     groups: {},
18594      /**
18595     * register a Navigation Group
18596     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18597     */
18598     register : function(navgrp)
18599     {
18600         this.groups[navgrp.navId] = navgrp;
18601         
18602     },
18603     /**
18604     * fetch a Navigation Group based on the navigation ID
18605     * if one does not exist , it will get created.
18606     * @param {string} the navgroup to add
18607     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18608     */
18609     get: function(navId) {
18610         if (typeof(this.groups[navId]) == 'undefined') {
18611             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18612         }
18613         return this.groups[navId] ;
18614     }
18615     
18616     
18617     
18618 });
18619
18620  /*
18621  * - LGPL
18622  *
18623  * TabPanel
18624  * 
18625  */
18626
18627 /**
18628  * @class Roo.bootstrap.TabPanel
18629  * @extends Roo.bootstrap.Component
18630  * Bootstrap TabPanel class
18631  * @cfg {Boolean} active panel active
18632  * @cfg {String} html panel content
18633  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18634  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18635  * @cfg {String} href click to link..
18636  * 
18637  * 
18638  * @constructor
18639  * Create a new TabPanel
18640  * @param {Object} config The config object
18641  */
18642
18643 Roo.bootstrap.TabPanel = function(config){
18644     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18645     this.addEvents({
18646         /**
18647              * @event changed
18648              * Fires when the active status changes
18649              * @param {Roo.bootstrap.TabPanel} this
18650              * @param {Boolean} state the new state
18651             
18652          */
18653         'changed': true,
18654         /**
18655              * @event beforedeactivate
18656              * Fires before a tab is de-activated - can be used to do validation on a form.
18657              * @param {Roo.bootstrap.TabPanel} this
18658              * @return {Boolean} false if there is an error
18659             
18660          */
18661         'beforedeactivate': true
18662      });
18663     
18664     this.tabId = this.tabId || Roo.id();
18665   
18666 };
18667
18668 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18669     
18670     active: false,
18671     html: false,
18672     tabId: false,
18673     navId : false,
18674     href : '',
18675     
18676     getAutoCreate : function(){
18677         
18678         
18679         var cfg = {
18680             tag: 'div',
18681             // item is needed for carousel - not sure if it has any effect otherwise
18682             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18683             html: this.html || ''
18684         };
18685         
18686         if(this.active){
18687             cfg.cls += ' active';
18688         }
18689         
18690         if(this.tabId){
18691             cfg.tabId = this.tabId;
18692         }
18693         
18694         
18695         
18696         return cfg;
18697     },
18698     
18699     initEvents:  function()
18700     {
18701         var p = this.parent();
18702         
18703         this.navId = this.navId || p.navId;
18704         
18705         if (typeof(this.navId) != 'undefined') {
18706             // not really needed.. but just in case.. parent should be a NavGroup.
18707             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18708             
18709             tg.register(this);
18710             
18711             var i = tg.tabs.length - 1;
18712             
18713             if(this.active && tg.bullets > 0 && i < tg.bullets){
18714                 tg.setActiveBullet(i);
18715             }
18716         }
18717         
18718         this.el.on('click', this.onClick, this);
18719         
18720         if(Roo.isTouch){
18721             this.el.on("touchstart", this.onTouchStart, this);
18722             this.el.on("touchmove", this.onTouchMove, this);
18723             this.el.on("touchend", this.onTouchEnd, this);
18724         }
18725         
18726     },
18727     
18728     onRender : function(ct, position)
18729     {
18730         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18731     },
18732     
18733     setActive : function(state)
18734     {
18735         Roo.log("panel - set active " + this.tabId + "=" + state);
18736         
18737         this.active = state;
18738         if (!state) {
18739             this.el.removeClass('active');
18740             
18741         } else  if (!this.el.hasClass('active')) {
18742             this.el.addClass('active');
18743         }
18744         
18745         this.fireEvent('changed', this, state);
18746     },
18747     
18748     onClick : function(e)
18749     {
18750         e.preventDefault();
18751         
18752         if(!this.href.length){
18753             return;
18754         }
18755         
18756         window.location.href = this.href;
18757     },
18758     
18759     startX : 0,
18760     startY : 0,
18761     endX : 0,
18762     endY : 0,
18763     swiping : false,
18764     
18765     onTouchStart : function(e)
18766     {
18767         this.swiping = false;
18768         
18769         this.startX = e.browserEvent.touches[0].clientX;
18770         this.startY = e.browserEvent.touches[0].clientY;
18771     },
18772     
18773     onTouchMove : function(e)
18774     {
18775         this.swiping = true;
18776         
18777         this.endX = e.browserEvent.touches[0].clientX;
18778         this.endY = e.browserEvent.touches[0].clientY;
18779     },
18780     
18781     onTouchEnd : function(e)
18782     {
18783         if(!this.swiping){
18784             this.onClick(e);
18785             return;
18786         }
18787         
18788         var tabGroup = this.parent();
18789         
18790         if(this.endX > this.startX){ // swiping right
18791             tabGroup.showPanelPrev();
18792             return;
18793         }
18794         
18795         if(this.startX > this.endX){ // swiping left
18796             tabGroup.showPanelNext();
18797             return;
18798         }
18799     }
18800     
18801     
18802 });
18803  
18804
18805  
18806
18807  /*
18808  * - LGPL
18809  *
18810  * DateField
18811  * 
18812  */
18813
18814 /**
18815  * @class Roo.bootstrap.DateField
18816  * @extends Roo.bootstrap.Input
18817  * Bootstrap DateField class
18818  * @cfg {Number} weekStart default 0
18819  * @cfg {String} viewMode default empty, (months|years)
18820  * @cfg {String} minViewMode default empty, (months|years)
18821  * @cfg {Number} startDate default -Infinity
18822  * @cfg {Number} endDate default Infinity
18823  * @cfg {Boolean} todayHighlight default false
18824  * @cfg {Boolean} todayBtn default false
18825  * @cfg {Boolean} calendarWeeks default false
18826  * @cfg {Object} daysOfWeekDisabled default empty
18827  * @cfg {Boolean} singleMode default false (true | false)
18828  * 
18829  * @cfg {Boolean} keyboardNavigation default true
18830  * @cfg {String} language default en
18831  * 
18832  * @constructor
18833  * Create a new DateField
18834  * @param {Object} config The config object
18835  */
18836
18837 Roo.bootstrap.DateField = function(config){
18838     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18839      this.addEvents({
18840             /**
18841              * @event show
18842              * Fires when this field show.
18843              * @param {Roo.bootstrap.DateField} this
18844              * @param {Mixed} date The date value
18845              */
18846             show : true,
18847             /**
18848              * @event show
18849              * Fires when this field hide.
18850              * @param {Roo.bootstrap.DateField} this
18851              * @param {Mixed} date The date value
18852              */
18853             hide : true,
18854             /**
18855              * @event select
18856              * Fires when select a date.
18857              * @param {Roo.bootstrap.DateField} this
18858              * @param {Mixed} date The date value
18859              */
18860             select : true,
18861             /**
18862              * @event beforeselect
18863              * Fires when before select a date.
18864              * @param {Roo.bootstrap.DateField} this
18865              * @param {Mixed} date The date value
18866              */
18867             beforeselect : true
18868         });
18869 };
18870
18871 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18872     
18873     /**
18874      * @cfg {String} format
18875      * The default date format string which can be overriden for localization support.  The format must be
18876      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18877      */
18878     format : "m/d/y",
18879     /**
18880      * @cfg {String} altFormats
18881      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18882      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18883      */
18884     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18885     
18886     weekStart : 0,
18887     
18888     viewMode : '',
18889     
18890     minViewMode : '',
18891     
18892     todayHighlight : false,
18893     
18894     todayBtn: false,
18895     
18896     language: 'en',
18897     
18898     keyboardNavigation: true,
18899     
18900     calendarWeeks: false,
18901     
18902     startDate: -Infinity,
18903     
18904     endDate: Infinity,
18905     
18906     daysOfWeekDisabled: [],
18907     
18908     _events: [],
18909     
18910     singleMode : false,
18911     
18912     UTCDate: function()
18913     {
18914         return new Date(Date.UTC.apply(Date, arguments));
18915     },
18916     
18917     UTCToday: function()
18918     {
18919         var today = new Date();
18920         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18921     },
18922     
18923     getDate: function() {
18924             var d = this.getUTCDate();
18925             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18926     },
18927     
18928     getUTCDate: function() {
18929             return this.date;
18930     },
18931     
18932     setDate: function(d) {
18933             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18934     },
18935     
18936     setUTCDate: function(d) {
18937             this.date = d;
18938             this.setValue(this.formatDate(this.date));
18939     },
18940         
18941     onRender: function(ct, position)
18942     {
18943         
18944         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18945         
18946         this.language = this.language || 'en';
18947         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18948         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18949         
18950         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18951         this.format = this.format || 'm/d/y';
18952         this.isInline = false;
18953         this.isInput = true;
18954         this.component = this.el.select('.add-on', true).first() || false;
18955         this.component = (this.component && this.component.length === 0) ? false : this.component;
18956         this.hasInput = this.component && this.inputEl().length;
18957         
18958         if (typeof(this.minViewMode === 'string')) {
18959             switch (this.minViewMode) {
18960                 case 'months':
18961                     this.minViewMode = 1;
18962                     break;
18963                 case 'years':
18964                     this.minViewMode = 2;
18965                     break;
18966                 default:
18967                     this.minViewMode = 0;
18968                     break;
18969             }
18970         }
18971         
18972         if (typeof(this.viewMode === 'string')) {
18973             switch (this.viewMode) {
18974                 case 'months':
18975                     this.viewMode = 1;
18976                     break;
18977                 case 'years':
18978                     this.viewMode = 2;
18979                     break;
18980                 default:
18981                     this.viewMode = 0;
18982                     break;
18983             }
18984         }
18985                 
18986         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18987         
18988 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18989         
18990         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18991         
18992         this.picker().on('mousedown', this.onMousedown, this);
18993         this.picker().on('click', this.onClick, this);
18994         
18995         this.picker().addClass('datepicker-dropdown');
18996         
18997         this.startViewMode = this.viewMode;
18998         
18999         if(this.singleMode){
19000             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
19001                 v.setVisibilityMode(Roo.Element.DISPLAY);
19002                 v.hide();
19003             });
19004             
19005             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19006                 v.setStyle('width', '189px');
19007             });
19008         }
19009         
19010         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
19011             if(!this.calendarWeeks){
19012                 v.remove();
19013                 return;
19014             }
19015             
19016             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19017             v.attr('colspan', function(i, val){
19018                 return parseInt(val) + 1;
19019             });
19020         });
19021                         
19022         
19023         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
19024         
19025         this.setStartDate(this.startDate);
19026         this.setEndDate(this.endDate);
19027         
19028         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19029         
19030         this.fillDow();
19031         this.fillMonths();
19032         this.update();
19033         this.showMode();
19034         
19035         if(this.isInline) {
19036             this.showPopup();
19037         }
19038     },
19039     
19040     picker : function()
19041     {
19042         return this.pickerEl;
19043 //        return this.el.select('.datepicker', true).first();
19044     },
19045     
19046     fillDow: function()
19047     {
19048         var dowCnt = this.weekStart;
19049         
19050         var dow = {
19051             tag: 'tr',
19052             cn: [
19053                 
19054             ]
19055         };
19056         
19057         if(this.calendarWeeks){
19058             dow.cn.push({
19059                 tag: 'th',
19060                 cls: 'cw',
19061                 html: '&nbsp;'
19062             })
19063         }
19064         
19065         while (dowCnt < this.weekStart + 7) {
19066             dow.cn.push({
19067                 tag: 'th',
19068                 cls: 'dow',
19069                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19070             });
19071         }
19072         
19073         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19074     },
19075     
19076     fillMonths: function()
19077     {    
19078         var i = 0;
19079         var months = this.picker().select('>.datepicker-months td', true).first();
19080         
19081         months.dom.innerHTML = '';
19082         
19083         while (i < 12) {
19084             var month = {
19085                 tag: 'span',
19086                 cls: 'month',
19087                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19088             };
19089             
19090             months.createChild(month);
19091         }
19092         
19093     },
19094     
19095     update: function()
19096     {
19097         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;
19098         
19099         if (this.date < this.startDate) {
19100             this.viewDate = new Date(this.startDate);
19101         } else if (this.date > this.endDate) {
19102             this.viewDate = new Date(this.endDate);
19103         } else {
19104             this.viewDate = new Date(this.date);
19105         }
19106         
19107         this.fill();
19108     },
19109     
19110     fill: function() 
19111     {
19112         var d = new Date(this.viewDate),
19113                 year = d.getUTCFullYear(),
19114                 month = d.getUTCMonth(),
19115                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19116                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19117                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19118                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19119                 currentDate = this.date && this.date.valueOf(),
19120                 today = this.UTCToday();
19121         
19122         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19123         
19124 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19125         
19126 //        this.picker.select('>tfoot th.today').
19127 //                                              .text(dates[this.language].today)
19128 //                                              .toggle(this.todayBtn !== false);
19129     
19130         this.updateNavArrows();
19131         this.fillMonths();
19132                                                 
19133         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19134         
19135         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19136          
19137         prevMonth.setUTCDate(day);
19138         
19139         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19140         
19141         var nextMonth = new Date(prevMonth);
19142         
19143         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19144         
19145         nextMonth = nextMonth.valueOf();
19146         
19147         var fillMonths = false;
19148         
19149         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19150         
19151         while(prevMonth.valueOf() <= nextMonth) {
19152             var clsName = '';
19153             
19154             if (prevMonth.getUTCDay() === this.weekStart) {
19155                 if(fillMonths){
19156                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19157                 }
19158                     
19159                 fillMonths = {
19160                     tag: 'tr',
19161                     cn: []
19162                 };
19163                 
19164                 if(this.calendarWeeks){
19165                     // ISO 8601: First week contains first thursday.
19166                     // ISO also states week starts on Monday, but we can be more abstract here.
19167                     var
19168                     // Start of current week: based on weekstart/current date
19169                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19170                     // Thursday of this week
19171                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19172                     // First Thursday of year, year from thursday
19173                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19174                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19175                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19176                     
19177                     fillMonths.cn.push({
19178                         tag: 'td',
19179                         cls: 'cw',
19180                         html: calWeek
19181                     });
19182                 }
19183             }
19184             
19185             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19186                 clsName += ' old';
19187             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19188                 clsName += ' new';
19189             }
19190             if (this.todayHighlight &&
19191                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19192                 prevMonth.getUTCMonth() == today.getMonth() &&
19193                 prevMonth.getUTCDate() == today.getDate()) {
19194                 clsName += ' today';
19195             }
19196             
19197             if (currentDate && prevMonth.valueOf() === currentDate) {
19198                 clsName += ' active';
19199             }
19200             
19201             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19202                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19203                     clsName += ' disabled';
19204             }
19205             
19206             fillMonths.cn.push({
19207                 tag: 'td',
19208                 cls: 'day ' + clsName,
19209                 html: prevMonth.getDate()
19210             });
19211             
19212             prevMonth.setDate(prevMonth.getDate()+1);
19213         }
19214           
19215         var currentYear = this.date && this.date.getUTCFullYear();
19216         var currentMonth = this.date && this.date.getUTCMonth();
19217         
19218         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19219         
19220         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19221             v.removeClass('active');
19222             
19223             if(currentYear === year && k === currentMonth){
19224                 v.addClass('active');
19225             }
19226             
19227             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19228                 v.addClass('disabled');
19229             }
19230             
19231         });
19232         
19233         
19234         year = parseInt(year/10, 10) * 10;
19235         
19236         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19237         
19238         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19239         
19240         year -= 1;
19241         for (var i = -1; i < 11; i++) {
19242             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19243                 tag: 'span',
19244                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19245                 html: year
19246             });
19247             
19248             year += 1;
19249         }
19250     },
19251     
19252     showMode: function(dir) 
19253     {
19254         if (dir) {
19255             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19256         }
19257         
19258         Roo.each(this.picker().select('>div',true).elements, function(v){
19259             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19260             v.hide();
19261         });
19262         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19263     },
19264     
19265     place: function()
19266     {
19267         if(this.isInline) {
19268             return;
19269         }
19270         
19271         this.picker().removeClass(['bottom', 'top']);
19272         
19273         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19274             /*
19275              * place to the top of element!
19276              *
19277              */
19278             
19279             this.picker().addClass('top');
19280             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19281             
19282             return;
19283         }
19284         
19285         this.picker().addClass('bottom');
19286         
19287         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19288     },
19289     
19290     parseDate : function(value)
19291     {
19292         if(!value || value instanceof Date){
19293             return value;
19294         }
19295         var v = Date.parseDate(value, this.format);
19296         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19297             v = Date.parseDate(value, 'Y-m-d');
19298         }
19299         if(!v && this.altFormats){
19300             if(!this.altFormatsArray){
19301                 this.altFormatsArray = this.altFormats.split("|");
19302             }
19303             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19304                 v = Date.parseDate(value, this.altFormatsArray[i]);
19305             }
19306         }
19307         return v;
19308     },
19309     
19310     formatDate : function(date, fmt)
19311     {   
19312         return (!date || !(date instanceof Date)) ?
19313         date : date.dateFormat(fmt || this.format);
19314     },
19315     
19316     onFocus : function()
19317     {
19318         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19319         this.showPopup();
19320     },
19321     
19322     onBlur : function()
19323     {
19324         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19325         
19326         var d = this.inputEl().getValue();
19327         
19328         this.setValue(d);
19329                 
19330         this.hidePopup();
19331     },
19332     
19333     showPopup : function()
19334     {
19335         this.picker().show();
19336         this.update();
19337         this.place();
19338         
19339         this.fireEvent('showpopup', this, this.date);
19340     },
19341     
19342     hidePopup : function()
19343     {
19344         if(this.isInline) {
19345             return;
19346         }
19347         this.picker().hide();
19348         this.viewMode = this.startViewMode;
19349         this.showMode();
19350         
19351         this.fireEvent('hidepopup', this, this.date);
19352         
19353     },
19354     
19355     onMousedown: function(e)
19356     {
19357         e.stopPropagation();
19358         e.preventDefault();
19359     },
19360     
19361     keyup: function(e)
19362     {
19363         Roo.bootstrap.DateField.superclass.keyup.call(this);
19364         this.update();
19365     },
19366
19367     setValue: function(v)
19368     {
19369         if(this.fireEvent('beforeselect', this, v) !== false){
19370             var d = new Date(this.parseDate(v) ).clearTime();
19371         
19372             if(isNaN(d.getTime())){
19373                 this.date = this.viewDate = '';
19374                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19375                 return;
19376             }
19377
19378             v = this.formatDate(d);
19379
19380             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19381
19382             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19383
19384             this.update();
19385
19386             this.fireEvent('select', this, this.date);
19387         }
19388     },
19389     
19390     getValue: function()
19391     {
19392         return this.formatDate(this.date);
19393     },
19394     
19395     fireKey: function(e)
19396     {
19397         if (!this.picker().isVisible()){
19398             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19399                 this.showPopup();
19400             }
19401             return;
19402         }
19403         
19404         var dateChanged = false,
19405         dir, day, month,
19406         newDate, newViewDate;
19407         
19408         switch(e.keyCode){
19409             case 27: // escape
19410                 this.hidePopup();
19411                 e.preventDefault();
19412                 break;
19413             case 37: // left
19414             case 39: // right
19415                 if (!this.keyboardNavigation) {
19416                     break;
19417                 }
19418                 dir = e.keyCode == 37 ? -1 : 1;
19419                 
19420                 if (e.ctrlKey){
19421                     newDate = this.moveYear(this.date, dir);
19422                     newViewDate = this.moveYear(this.viewDate, dir);
19423                 } else if (e.shiftKey){
19424                     newDate = this.moveMonth(this.date, dir);
19425                     newViewDate = this.moveMonth(this.viewDate, dir);
19426                 } else {
19427                     newDate = new Date(this.date);
19428                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19429                     newViewDate = new Date(this.viewDate);
19430                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19431                 }
19432                 if (this.dateWithinRange(newDate)){
19433                     this.date = newDate;
19434                     this.viewDate = newViewDate;
19435                     this.setValue(this.formatDate(this.date));
19436 //                    this.update();
19437                     e.preventDefault();
19438                     dateChanged = true;
19439                 }
19440                 break;
19441             case 38: // up
19442             case 40: // down
19443                 if (!this.keyboardNavigation) {
19444                     break;
19445                 }
19446                 dir = e.keyCode == 38 ? -1 : 1;
19447                 if (e.ctrlKey){
19448                     newDate = this.moveYear(this.date, dir);
19449                     newViewDate = this.moveYear(this.viewDate, dir);
19450                 } else if (e.shiftKey){
19451                     newDate = this.moveMonth(this.date, dir);
19452                     newViewDate = this.moveMonth(this.viewDate, dir);
19453                 } else {
19454                     newDate = new Date(this.date);
19455                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19456                     newViewDate = new Date(this.viewDate);
19457                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19458                 }
19459                 if (this.dateWithinRange(newDate)){
19460                     this.date = newDate;
19461                     this.viewDate = newViewDate;
19462                     this.setValue(this.formatDate(this.date));
19463 //                    this.update();
19464                     e.preventDefault();
19465                     dateChanged = true;
19466                 }
19467                 break;
19468             case 13: // enter
19469                 this.setValue(this.formatDate(this.date));
19470                 this.hidePopup();
19471                 e.preventDefault();
19472                 break;
19473             case 9: // tab
19474                 this.setValue(this.formatDate(this.date));
19475                 this.hidePopup();
19476                 break;
19477             case 16: // shift
19478             case 17: // ctrl
19479             case 18: // alt
19480                 break;
19481             default :
19482                 this.hidePopup();
19483                 
19484         }
19485     },
19486     
19487     
19488     onClick: function(e) 
19489     {
19490         e.stopPropagation();
19491         e.preventDefault();
19492         
19493         var target = e.getTarget();
19494         
19495         if(target.nodeName.toLowerCase() === 'i'){
19496             target = Roo.get(target).dom.parentNode;
19497         }
19498         
19499         var nodeName = target.nodeName;
19500         var className = target.className;
19501         var html = target.innerHTML;
19502         //Roo.log(nodeName);
19503         
19504         switch(nodeName.toLowerCase()) {
19505             case 'th':
19506                 switch(className) {
19507                     case 'switch':
19508                         this.showMode(1);
19509                         break;
19510                     case 'prev':
19511                     case 'next':
19512                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19513                         switch(this.viewMode){
19514                                 case 0:
19515                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19516                                         break;
19517                                 case 1:
19518                                 case 2:
19519                                         this.viewDate = this.moveYear(this.viewDate, dir);
19520                                         break;
19521                         }
19522                         this.fill();
19523                         break;
19524                     case 'today':
19525                         var date = new Date();
19526                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19527 //                        this.fill()
19528                         this.setValue(this.formatDate(this.date));
19529                         
19530                         this.hidePopup();
19531                         break;
19532                 }
19533                 break;
19534             case 'span':
19535                 if (className.indexOf('disabled') < 0) {
19536                     this.viewDate.setUTCDate(1);
19537                     if (className.indexOf('month') > -1) {
19538                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19539                     } else {
19540                         var year = parseInt(html, 10) || 0;
19541                         this.viewDate.setUTCFullYear(year);
19542                         
19543                     }
19544                     
19545                     if(this.singleMode){
19546                         this.setValue(this.formatDate(this.viewDate));
19547                         this.hidePopup();
19548                         return;
19549                     }
19550                     
19551                     this.showMode(-1);
19552                     this.fill();
19553                 }
19554                 break;
19555                 
19556             case 'td':
19557                 //Roo.log(className);
19558                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19559                     var day = parseInt(html, 10) || 1;
19560                     var year = this.viewDate.getUTCFullYear(),
19561                         month = this.viewDate.getUTCMonth();
19562
19563                     if (className.indexOf('old') > -1) {
19564                         if(month === 0 ){
19565                             month = 11;
19566                             year -= 1;
19567                         }else{
19568                             month -= 1;
19569                         }
19570                     } else if (className.indexOf('new') > -1) {
19571                         if (month == 11) {
19572                             month = 0;
19573                             year += 1;
19574                         } else {
19575                             month += 1;
19576                         }
19577                     }
19578                     //Roo.log([year,month,day]);
19579                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19580                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19581 //                    this.fill();
19582                     //Roo.log(this.formatDate(this.date));
19583                     this.setValue(this.formatDate(this.date));
19584                     this.hidePopup();
19585                 }
19586                 break;
19587         }
19588     },
19589     
19590     setStartDate: function(startDate)
19591     {
19592         this.startDate = startDate || -Infinity;
19593         if (this.startDate !== -Infinity) {
19594             this.startDate = this.parseDate(this.startDate);
19595         }
19596         this.update();
19597         this.updateNavArrows();
19598     },
19599
19600     setEndDate: function(endDate)
19601     {
19602         this.endDate = endDate || Infinity;
19603         if (this.endDate !== Infinity) {
19604             this.endDate = this.parseDate(this.endDate);
19605         }
19606         this.update();
19607         this.updateNavArrows();
19608     },
19609     
19610     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19611     {
19612         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19613         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19614             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19615         }
19616         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19617             return parseInt(d, 10);
19618         });
19619         this.update();
19620         this.updateNavArrows();
19621     },
19622     
19623     updateNavArrows: function() 
19624     {
19625         if(this.singleMode){
19626             return;
19627         }
19628         
19629         var d = new Date(this.viewDate),
19630         year = d.getUTCFullYear(),
19631         month = d.getUTCMonth();
19632         
19633         Roo.each(this.picker().select('.prev', true).elements, function(v){
19634             v.show();
19635             switch (this.viewMode) {
19636                 case 0:
19637
19638                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19639                         v.hide();
19640                     }
19641                     break;
19642                 case 1:
19643                 case 2:
19644                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19645                         v.hide();
19646                     }
19647                     break;
19648             }
19649         });
19650         
19651         Roo.each(this.picker().select('.next', true).elements, function(v){
19652             v.show();
19653             switch (this.viewMode) {
19654                 case 0:
19655
19656                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19657                         v.hide();
19658                     }
19659                     break;
19660                 case 1:
19661                 case 2:
19662                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19663                         v.hide();
19664                     }
19665                     break;
19666             }
19667         })
19668     },
19669     
19670     moveMonth: function(date, dir)
19671     {
19672         if (!dir) {
19673             return date;
19674         }
19675         var new_date = new Date(date.valueOf()),
19676         day = new_date.getUTCDate(),
19677         month = new_date.getUTCMonth(),
19678         mag = Math.abs(dir),
19679         new_month, test;
19680         dir = dir > 0 ? 1 : -1;
19681         if (mag == 1){
19682             test = dir == -1
19683             // If going back one month, make sure month is not current month
19684             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19685             ? function(){
19686                 return new_date.getUTCMonth() == month;
19687             }
19688             // If going forward one month, make sure month is as expected
19689             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19690             : function(){
19691                 return new_date.getUTCMonth() != new_month;
19692             };
19693             new_month = month + dir;
19694             new_date.setUTCMonth(new_month);
19695             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19696             if (new_month < 0 || new_month > 11) {
19697                 new_month = (new_month + 12) % 12;
19698             }
19699         } else {
19700             // For magnitudes >1, move one month at a time...
19701             for (var i=0; i<mag; i++) {
19702                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19703                 new_date = this.moveMonth(new_date, dir);
19704             }
19705             // ...then reset the day, keeping it in the new month
19706             new_month = new_date.getUTCMonth();
19707             new_date.setUTCDate(day);
19708             test = function(){
19709                 return new_month != new_date.getUTCMonth();
19710             };
19711         }
19712         // Common date-resetting loop -- if date is beyond end of month, make it
19713         // end of month
19714         while (test()){
19715             new_date.setUTCDate(--day);
19716             new_date.setUTCMonth(new_month);
19717         }
19718         return new_date;
19719     },
19720
19721     moveYear: function(date, dir)
19722     {
19723         return this.moveMonth(date, dir*12);
19724     },
19725
19726     dateWithinRange: function(date)
19727     {
19728         return date >= this.startDate && date <= this.endDate;
19729     },
19730
19731     
19732     remove: function() 
19733     {
19734         this.picker().remove();
19735     },
19736     
19737     validateValue : function(value)
19738     {
19739         if(this.getVisibilityEl().hasClass('hidden')){
19740             return true;
19741         }
19742         
19743         if(value.length < 1)  {
19744             if(this.allowBlank){
19745                 return true;
19746             }
19747             return false;
19748         }
19749         
19750         if(value.length < this.minLength){
19751             return false;
19752         }
19753         if(value.length > this.maxLength){
19754             return false;
19755         }
19756         if(this.vtype){
19757             var vt = Roo.form.VTypes;
19758             if(!vt[this.vtype](value, this)){
19759                 return false;
19760             }
19761         }
19762         if(typeof this.validator == "function"){
19763             var msg = this.validator(value);
19764             if(msg !== true){
19765                 return false;
19766             }
19767         }
19768         
19769         if(this.regex && !this.regex.test(value)){
19770             return false;
19771         }
19772         
19773         if(typeof(this.parseDate(value)) == 'undefined'){
19774             return false;
19775         }
19776         
19777         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19778             return false;
19779         }      
19780         
19781         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19782             return false;
19783         } 
19784         
19785         
19786         return true;
19787     },
19788     
19789     reset : function()
19790     {
19791         this.date = this.viewDate = '';
19792         
19793         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19794     }
19795    
19796 });
19797
19798 Roo.apply(Roo.bootstrap.DateField,  {
19799     
19800     head : {
19801         tag: 'thead',
19802         cn: [
19803         {
19804             tag: 'tr',
19805             cn: [
19806             {
19807                 tag: 'th',
19808                 cls: 'prev',
19809                 html: '<i class="fa fa-arrow-left"/>'
19810             },
19811             {
19812                 tag: 'th',
19813                 cls: 'switch',
19814                 colspan: '5'
19815             },
19816             {
19817                 tag: 'th',
19818                 cls: 'next',
19819                 html: '<i class="fa fa-arrow-right"/>'
19820             }
19821
19822             ]
19823         }
19824         ]
19825     },
19826     
19827     content : {
19828         tag: 'tbody',
19829         cn: [
19830         {
19831             tag: 'tr',
19832             cn: [
19833             {
19834                 tag: 'td',
19835                 colspan: '7'
19836             }
19837             ]
19838         }
19839         ]
19840     },
19841     
19842     footer : {
19843         tag: 'tfoot',
19844         cn: [
19845         {
19846             tag: 'tr',
19847             cn: [
19848             {
19849                 tag: 'th',
19850                 colspan: '7',
19851                 cls: 'today'
19852             }
19853                     
19854             ]
19855         }
19856         ]
19857     },
19858     
19859     dates:{
19860         en: {
19861             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19862             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19863             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19864             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19865             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19866             today: "Today"
19867         }
19868     },
19869     
19870     modes: [
19871     {
19872         clsName: 'days',
19873         navFnc: 'Month',
19874         navStep: 1
19875     },
19876     {
19877         clsName: 'months',
19878         navFnc: 'FullYear',
19879         navStep: 1
19880     },
19881     {
19882         clsName: 'years',
19883         navFnc: 'FullYear',
19884         navStep: 10
19885     }]
19886 });
19887
19888 Roo.apply(Roo.bootstrap.DateField,  {
19889   
19890     template : {
19891         tag: 'div',
19892         cls: 'datepicker dropdown-menu roo-dynamic',
19893         cn: [
19894         {
19895             tag: 'div',
19896             cls: 'datepicker-days',
19897             cn: [
19898             {
19899                 tag: 'table',
19900                 cls: 'table-condensed',
19901                 cn:[
19902                 Roo.bootstrap.DateField.head,
19903                 {
19904                     tag: 'tbody'
19905                 },
19906                 Roo.bootstrap.DateField.footer
19907                 ]
19908             }
19909             ]
19910         },
19911         {
19912             tag: 'div',
19913             cls: 'datepicker-months',
19914             cn: [
19915             {
19916                 tag: 'table',
19917                 cls: 'table-condensed',
19918                 cn:[
19919                 Roo.bootstrap.DateField.head,
19920                 Roo.bootstrap.DateField.content,
19921                 Roo.bootstrap.DateField.footer
19922                 ]
19923             }
19924             ]
19925         },
19926         {
19927             tag: 'div',
19928             cls: 'datepicker-years',
19929             cn: [
19930             {
19931                 tag: 'table',
19932                 cls: 'table-condensed',
19933                 cn:[
19934                 Roo.bootstrap.DateField.head,
19935                 Roo.bootstrap.DateField.content,
19936                 Roo.bootstrap.DateField.footer
19937                 ]
19938             }
19939             ]
19940         }
19941         ]
19942     }
19943 });
19944
19945  
19946
19947  /*
19948  * - LGPL
19949  *
19950  * TimeField
19951  * 
19952  */
19953
19954 /**
19955  * @class Roo.bootstrap.TimeField
19956  * @extends Roo.bootstrap.Input
19957  * Bootstrap DateField class
19958  * 
19959  * 
19960  * @constructor
19961  * Create a new TimeField
19962  * @param {Object} config The config object
19963  */
19964
19965 Roo.bootstrap.TimeField = function(config){
19966     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19967     this.addEvents({
19968             /**
19969              * @event show
19970              * Fires when this field show.
19971              * @param {Roo.bootstrap.DateField} thisthis
19972              * @param {Mixed} date The date value
19973              */
19974             show : true,
19975             /**
19976              * @event show
19977              * Fires when this field hide.
19978              * @param {Roo.bootstrap.DateField} this
19979              * @param {Mixed} date The date value
19980              */
19981             hide : true,
19982             /**
19983              * @event select
19984              * Fires when select a date.
19985              * @param {Roo.bootstrap.DateField} this
19986              * @param {Mixed} date The date value
19987              */
19988             select : true
19989         });
19990 };
19991
19992 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19993     
19994     /**
19995      * @cfg {String} format
19996      * The default time format string which can be overriden for localization support.  The format must be
19997      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19998      */
19999     format : "H:i",
20000        
20001     onRender: function(ct, position)
20002     {
20003         
20004         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
20005                 
20006         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
20007         
20008         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20009         
20010         this.pop = this.picker().select('>.datepicker-time',true).first();
20011         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20012         
20013         this.picker().on('mousedown', this.onMousedown, this);
20014         this.picker().on('click', this.onClick, this);
20015         
20016         this.picker().addClass('datepicker-dropdown');
20017     
20018         this.fillTime();
20019         this.update();
20020             
20021         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
20022         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
20023         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
20024         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
20025         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
20026         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20027
20028     },
20029     
20030     fireKey: function(e){
20031         if (!this.picker().isVisible()){
20032             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20033                 this.show();
20034             }
20035             return;
20036         }
20037
20038         e.preventDefault();
20039         
20040         switch(e.keyCode){
20041             case 27: // escape
20042                 this.hide();
20043                 break;
20044             case 37: // left
20045             case 39: // right
20046                 this.onTogglePeriod();
20047                 break;
20048             case 38: // up
20049                 this.onIncrementMinutes();
20050                 break;
20051             case 40: // down
20052                 this.onDecrementMinutes();
20053                 break;
20054             case 13: // enter
20055             case 9: // tab
20056                 this.setTime();
20057                 break;
20058         }
20059     },
20060     
20061     onClick: function(e) {
20062         e.stopPropagation();
20063         e.preventDefault();
20064     },
20065     
20066     picker : function()
20067     {
20068         return this.el.select('.datepicker', true).first();
20069     },
20070     
20071     fillTime: function()
20072     {    
20073         var time = this.pop.select('tbody', true).first();
20074         
20075         time.dom.innerHTML = '';
20076         
20077         time.createChild({
20078             tag: 'tr',
20079             cn: [
20080                 {
20081                     tag: 'td',
20082                     cn: [
20083                         {
20084                             tag: 'a',
20085                             href: '#',
20086                             cls: 'btn',
20087                             cn: [
20088                                 {
20089                                     tag: 'span',
20090                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20091                                 }
20092                             ]
20093                         } 
20094                     ]
20095                 },
20096                 {
20097                     tag: 'td',
20098                     cls: 'separator'
20099                 },
20100                 {
20101                     tag: 'td',
20102                     cn: [
20103                         {
20104                             tag: 'a',
20105                             href: '#',
20106                             cls: 'btn',
20107                             cn: [
20108                                 {
20109                                     tag: 'span',
20110                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20111                                 }
20112                             ]
20113                         }
20114                     ]
20115                 },
20116                 {
20117                     tag: 'td',
20118                     cls: 'separator'
20119                 }
20120             ]
20121         });
20122         
20123         time.createChild({
20124             tag: 'tr',
20125             cn: [
20126                 {
20127                     tag: 'td',
20128                     cn: [
20129                         {
20130                             tag: 'span',
20131                             cls: 'timepicker-hour',
20132                             html: '00'
20133                         }  
20134                     ]
20135                 },
20136                 {
20137                     tag: 'td',
20138                     cls: 'separator',
20139                     html: ':'
20140                 },
20141                 {
20142                     tag: 'td',
20143                     cn: [
20144                         {
20145                             tag: 'span',
20146                             cls: 'timepicker-minute',
20147                             html: '00'
20148                         }  
20149                     ]
20150                 },
20151                 {
20152                     tag: 'td',
20153                     cls: 'separator'
20154                 },
20155                 {
20156                     tag: 'td',
20157                     cn: [
20158                         {
20159                             tag: 'button',
20160                             type: 'button',
20161                             cls: 'btn btn-primary period',
20162                             html: 'AM'
20163                             
20164                         }
20165                     ]
20166                 }
20167             ]
20168         });
20169         
20170         time.createChild({
20171             tag: 'tr',
20172             cn: [
20173                 {
20174                     tag: 'td',
20175                     cn: [
20176                         {
20177                             tag: 'a',
20178                             href: '#',
20179                             cls: 'btn',
20180                             cn: [
20181                                 {
20182                                     tag: 'span',
20183                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20184                                 }
20185                             ]
20186                         }
20187                     ]
20188                 },
20189                 {
20190                     tag: 'td',
20191                     cls: 'separator'
20192                 },
20193                 {
20194                     tag: 'td',
20195                     cn: [
20196                         {
20197                             tag: 'a',
20198                             href: '#',
20199                             cls: 'btn',
20200                             cn: [
20201                                 {
20202                                     tag: 'span',
20203                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20204                                 }
20205                             ]
20206                         }
20207                     ]
20208                 },
20209                 {
20210                     tag: 'td',
20211                     cls: 'separator'
20212                 }
20213             ]
20214         });
20215         
20216     },
20217     
20218     update: function()
20219     {
20220         
20221         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20222         
20223         this.fill();
20224     },
20225     
20226     fill: function() 
20227     {
20228         var hours = this.time.getHours();
20229         var minutes = this.time.getMinutes();
20230         var period = 'AM';
20231         
20232         if(hours > 11){
20233             period = 'PM';
20234         }
20235         
20236         if(hours == 0){
20237             hours = 12;
20238         }
20239         
20240         
20241         if(hours > 12){
20242             hours = hours - 12;
20243         }
20244         
20245         if(hours < 10){
20246             hours = '0' + hours;
20247         }
20248         
20249         if(minutes < 10){
20250             minutes = '0' + minutes;
20251         }
20252         
20253         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20254         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20255         this.pop.select('button', true).first().dom.innerHTML = period;
20256         
20257     },
20258     
20259     place: function()
20260     {   
20261         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20262         
20263         var cls = ['bottom'];
20264         
20265         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20266             cls.pop();
20267             cls.push('top');
20268         }
20269         
20270         cls.push('right');
20271         
20272         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20273             cls.pop();
20274             cls.push('left');
20275         }
20276         
20277         this.picker().addClass(cls.join('-'));
20278         
20279         var _this = this;
20280         
20281         Roo.each(cls, function(c){
20282             if(c == 'bottom'){
20283                 _this.picker().setTop(_this.inputEl().getHeight());
20284                 return;
20285             }
20286             if(c == 'top'){
20287                 _this.picker().setTop(0 - _this.picker().getHeight());
20288                 return;
20289             }
20290             
20291             if(c == 'left'){
20292                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20293                 return;
20294             }
20295             if(c == 'right'){
20296                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20297                 return;
20298             }
20299         });
20300         
20301     },
20302   
20303     onFocus : function()
20304     {
20305         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20306         this.show();
20307     },
20308     
20309     onBlur : function()
20310     {
20311         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20312         this.hide();
20313     },
20314     
20315     show : function()
20316     {
20317         this.picker().show();
20318         this.pop.show();
20319         this.update();
20320         this.place();
20321         
20322         this.fireEvent('show', this, this.date);
20323     },
20324     
20325     hide : function()
20326     {
20327         this.picker().hide();
20328         this.pop.hide();
20329         
20330         this.fireEvent('hide', this, this.date);
20331     },
20332     
20333     setTime : function()
20334     {
20335         this.hide();
20336         this.setValue(this.time.format(this.format));
20337         
20338         this.fireEvent('select', this, this.date);
20339         
20340         
20341     },
20342     
20343     onMousedown: function(e){
20344         e.stopPropagation();
20345         e.preventDefault();
20346     },
20347     
20348     onIncrementHours: function()
20349     {
20350         Roo.log('onIncrementHours');
20351         this.time = this.time.add(Date.HOUR, 1);
20352         this.update();
20353         
20354     },
20355     
20356     onDecrementHours: function()
20357     {
20358         Roo.log('onDecrementHours');
20359         this.time = this.time.add(Date.HOUR, -1);
20360         this.update();
20361     },
20362     
20363     onIncrementMinutes: function()
20364     {
20365         Roo.log('onIncrementMinutes');
20366         this.time = this.time.add(Date.MINUTE, 1);
20367         this.update();
20368     },
20369     
20370     onDecrementMinutes: function()
20371     {
20372         Roo.log('onDecrementMinutes');
20373         this.time = this.time.add(Date.MINUTE, -1);
20374         this.update();
20375     },
20376     
20377     onTogglePeriod: function()
20378     {
20379         Roo.log('onTogglePeriod');
20380         this.time = this.time.add(Date.HOUR, 12);
20381         this.update();
20382     }
20383     
20384    
20385 });
20386
20387 Roo.apply(Roo.bootstrap.TimeField,  {
20388     
20389     content : {
20390         tag: 'tbody',
20391         cn: [
20392             {
20393                 tag: 'tr',
20394                 cn: [
20395                 {
20396                     tag: 'td',
20397                     colspan: '7'
20398                 }
20399                 ]
20400             }
20401         ]
20402     },
20403     
20404     footer : {
20405         tag: 'tfoot',
20406         cn: [
20407             {
20408                 tag: 'tr',
20409                 cn: [
20410                 {
20411                     tag: 'th',
20412                     colspan: '7',
20413                     cls: '',
20414                     cn: [
20415                         {
20416                             tag: 'button',
20417                             cls: 'btn btn-info ok',
20418                             html: 'OK'
20419                         }
20420                     ]
20421                 }
20422
20423                 ]
20424             }
20425         ]
20426     }
20427 });
20428
20429 Roo.apply(Roo.bootstrap.TimeField,  {
20430   
20431     template : {
20432         tag: 'div',
20433         cls: 'datepicker dropdown-menu',
20434         cn: [
20435             {
20436                 tag: 'div',
20437                 cls: 'datepicker-time',
20438                 cn: [
20439                 {
20440                     tag: 'table',
20441                     cls: 'table-condensed',
20442                     cn:[
20443                     Roo.bootstrap.TimeField.content,
20444                     Roo.bootstrap.TimeField.footer
20445                     ]
20446                 }
20447                 ]
20448             }
20449         ]
20450     }
20451 });
20452
20453  
20454
20455  /*
20456  * - LGPL
20457  *
20458  * MonthField
20459  * 
20460  */
20461
20462 /**
20463  * @class Roo.bootstrap.MonthField
20464  * @extends Roo.bootstrap.Input
20465  * Bootstrap MonthField class
20466  * 
20467  * @cfg {String} language default en
20468  * 
20469  * @constructor
20470  * Create a new MonthField
20471  * @param {Object} config The config object
20472  */
20473
20474 Roo.bootstrap.MonthField = function(config){
20475     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20476     
20477     this.addEvents({
20478         /**
20479          * @event show
20480          * Fires when this field show.
20481          * @param {Roo.bootstrap.MonthField} this
20482          * @param {Mixed} date The date value
20483          */
20484         show : true,
20485         /**
20486          * @event show
20487          * Fires when this field hide.
20488          * @param {Roo.bootstrap.MonthField} this
20489          * @param {Mixed} date The date value
20490          */
20491         hide : true,
20492         /**
20493          * @event select
20494          * Fires when select a date.
20495          * @param {Roo.bootstrap.MonthField} this
20496          * @param {String} oldvalue The old value
20497          * @param {String} newvalue The new value
20498          */
20499         select : true
20500     });
20501 };
20502
20503 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20504     
20505     onRender: function(ct, position)
20506     {
20507         
20508         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20509         
20510         this.language = this.language || 'en';
20511         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20512         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20513         
20514         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20515         this.isInline = false;
20516         this.isInput = true;
20517         this.component = this.el.select('.add-on', true).first() || false;
20518         this.component = (this.component && this.component.length === 0) ? false : this.component;
20519         this.hasInput = this.component && this.inputEL().length;
20520         
20521         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20522         
20523         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20524         
20525         this.picker().on('mousedown', this.onMousedown, this);
20526         this.picker().on('click', this.onClick, this);
20527         
20528         this.picker().addClass('datepicker-dropdown');
20529         
20530         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20531             v.setStyle('width', '189px');
20532         });
20533         
20534         this.fillMonths();
20535         
20536         this.update();
20537         
20538         if(this.isInline) {
20539             this.show();
20540         }
20541         
20542     },
20543     
20544     setValue: function(v, suppressEvent)
20545     {   
20546         var o = this.getValue();
20547         
20548         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20549         
20550         this.update();
20551
20552         if(suppressEvent !== true){
20553             this.fireEvent('select', this, o, v);
20554         }
20555         
20556     },
20557     
20558     getValue: function()
20559     {
20560         return this.value;
20561     },
20562     
20563     onClick: function(e) 
20564     {
20565         e.stopPropagation();
20566         e.preventDefault();
20567         
20568         var target = e.getTarget();
20569         
20570         if(target.nodeName.toLowerCase() === 'i'){
20571             target = Roo.get(target).dom.parentNode;
20572         }
20573         
20574         var nodeName = target.nodeName;
20575         var className = target.className;
20576         var html = target.innerHTML;
20577         
20578         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20579             return;
20580         }
20581         
20582         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20583         
20584         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20585         
20586         this.hide();
20587                         
20588     },
20589     
20590     picker : function()
20591     {
20592         return this.pickerEl;
20593     },
20594     
20595     fillMonths: function()
20596     {    
20597         var i = 0;
20598         var months = this.picker().select('>.datepicker-months td', true).first();
20599         
20600         months.dom.innerHTML = '';
20601         
20602         while (i < 12) {
20603             var month = {
20604                 tag: 'span',
20605                 cls: 'month',
20606                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20607             };
20608             
20609             months.createChild(month);
20610         }
20611         
20612     },
20613     
20614     update: function()
20615     {
20616         var _this = this;
20617         
20618         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20619             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20620         }
20621         
20622         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20623             e.removeClass('active');
20624             
20625             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20626                 e.addClass('active');
20627             }
20628         })
20629     },
20630     
20631     place: function()
20632     {
20633         if(this.isInline) {
20634             return;
20635         }
20636         
20637         this.picker().removeClass(['bottom', 'top']);
20638         
20639         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20640             /*
20641              * place to the top of element!
20642              *
20643              */
20644             
20645             this.picker().addClass('top');
20646             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20647             
20648             return;
20649         }
20650         
20651         this.picker().addClass('bottom');
20652         
20653         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20654     },
20655     
20656     onFocus : function()
20657     {
20658         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20659         this.show();
20660     },
20661     
20662     onBlur : function()
20663     {
20664         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20665         
20666         var d = this.inputEl().getValue();
20667         
20668         this.setValue(d);
20669                 
20670         this.hide();
20671     },
20672     
20673     show : function()
20674     {
20675         this.picker().show();
20676         this.picker().select('>.datepicker-months', true).first().show();
20677         this.update();
20678         this.place();
20679         
20680         this.fireEvent('show', this, this.date);
20681     },
20682     
20683     hide : function()
20684     {
20685         if(this.isInline) {
20686             return;
20687         }
20688         this.picker().hide();
20689         this.fireEvent('hide', this, this.date);
20690         
20691     },
20692     
20693     onMousedown: function(e)
20694     {
20695         e.stopPropagation();
20696         e.preventDefault();
20697     },
20698     
20699     keyup: function(e)
20700     {
20701         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20702         this.update();
20703     },
20704
20705     fireKey: function(e)
20706     {
20707         if (!this.picker().isVisible()){
20708             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20709                 this.show();
20710             }
20711             return;
20712         }
20713         
20714         var dir;
20715         
20716         switch(e.keyCode){
20717             case 27: // escape
20718                 this.hide();
20719                 e.preventDefault();
20720                 break;
20721             case 37: // left
20722             case 39: // right
20723                 dir = e.keyCode == 37 ? -1 : 1;
20724                 
20725                 this.vIndex = this.vIndex + dir;
20726                 
20727                 if(this.vIndex < 0){
20728                     this.vIndex = 0;
20729                 }
20730                 
20731                 if(this.vIndex > 11){
20732                     this.vIndex = 11;
20733                 }
20734                 
20735                 if(isNaN(this.vIndex)){
20736                     this.vIndex = 0;
20737                 }
20738                 
20739                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20740                 
20741                 break;
20742             case 38: // up
20743             case 40: // down
20744                 
20745                 dir = e.keyCode == 38 ? -1 : 1;
20746                 
20747                 this.vIndex = this.vIndex + dir * 4;
20748                 
20749                 if(this.vIndex < 0){
20750                     this.vIndex = 0;
20751                 }
20752                 
20753                 if(this.vIndex > 11){
20754                     this.vIndex = 11;
20755                 }
20756                 
20757                 if(isNaN(this.vIndex)){
20758                     this.vIndex = 0;
20759                 }
20760                 
20761                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20762                 break;
20763                 
20764             case 13: // enter
20765                 
20766                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20767                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20768                 }
20769                 
20770                 this.hide();
20771                 e.preventDefault();
20772                 break;
20773             case 9: // tab
20774                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20775                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20776                 }
20777                 this.hide();
20778                 break;
20779             case 16: // shift
20780             case 17: // ctrl
20781             case 18: // alt
20782                 break;
20783             default :
20784                 this.hide();
20785                 
20786         }
20787     },
20788     
20789     remove: function() 
20790     {
20791         this.picker().remove();
20792     }
20793    
20794 });
20795
20796 Roo.apply(Roo.bootstrap.MonthField,  {
20797     
20798     content : {
20799         tag: 'tbody',
20800         cn: [
20801         {
20802             tag: 'tr',
20803             cn: [
20804             {
20805                 tag: 'td',
20806                 colspan: '7'
20807             }
20808             ]
20809         }
20810         ]
20811     },
20812     
20813     dates:{
20814         en: {
20815             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20816             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20817         }
20818     }
20819 });
20820
20821 Roo.apply(Roo.bootstrap.MonthField,  {
20822   
20823     template : {
20824         tag: 'div',
20825         cls: 'datepicker dropdown-menu roo-dynamic',
20826         cn: [
20827             {
20828                 tag: 'div',
20829                 cls: 'datepicker-months',
20830                 cn: [
20831                 {
20832                     tag: 'table',
20833                     cls: 'table-condensed',
20834                     cn:[
20835                         Roo.bootstrap.DateField.content
20836                     ]
20837                 }
20838                 ]
20839             }
20840         ]
20841     }
20842 });
20843
20844  
20845
20846  
20847  /*
20848  * - LGPL
20849  *
20850  * CheckBox
20851  * 
20852  */
20853
20854 /**
20855  * @class Roo.bootstrap.CheckBox
20856  * @extends Roo.bootstrap.Input
20857  * Bootstrap CheckBox class
20858  * 
20859  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20860  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20861  * @cfg {String} boxLabel The text that appears beside the checkbox
20862  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20863  * @cfg {Boolean} checked initnal the element
20864  * @cfg {Boolean} inline inline the element (default false)
20865  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20866  * @cfg {String} tooltip label tooltip
20867  * 
20868  * @constructor
20869  * Create a new CheckBox
20870  * @param {Object} config The config object
20871  */
20872
20873 Roo.bootstrap.CheckBox = function(config){
20874     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20875    
20876     this.addEvents({
20877         /**
20878         * @event check
20879         * Fires when the element is checked or unchecked.
20880         * @param {Roo.bootstrap.CheckBox} this This input
20881         * @param {Boolean} checked The new checked value
20882         */
20883        check : true,
20884        /**
20885         * @event click
20886         * Fires when the element is click.
20887         * @param {Roo.bootstrap.CheckBox} this This input
20888         */
20889        click : true
20890     });
20891     
20892 };
20893
20894 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20895   
20896     inputType: 'checkbox',
20897     inputValue: 1,
20898     valueOff: 0,
20899     boxLabel: false,
20900     checked: false,
20901     weight : false,
20902     inline: false,
20903     tooltip : '',
20904     
20905     // checkbox success does not make any sense really.. 
20906     invalidClass : "",
20907     validClass : "",
20908     
20909     
20910     getAutoCreate : function()
20911     {
20912         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20913         
20914         var id = Roo.id();
20915         
20916         var cfg = {};
20917         
20918         cfg.cls = 'form-group ' + this.inputType; //input-group
20919         
20920         if(this.inline){
20921             cfg.cls += ' ' + this.inputType + '-inline';
20922         }
20923         
20924         var input =  {
20925             tag: 'input',
20926             id : id,
20927             type : this.inputType,
20928             value : this.inputValue,
20929             cls : 'roo-' + this.inputType, //'form-box',
20930             placeholder : this.placeholder || ''
20931             
20932         };
20933         
20934         if(this.inputType != 'radio'){
20935             var hidden =  {
20936                 tag: 'input',
20937                 type : 'hidden',
20938                 cls : 'roo-hidden-value',
20939                 value : this.checked ? this.inputValue : this.valueOff
20940             };
20941         }
20942         
20943             
20944         if (this.weight) { // Validity check?
20945             cfg.cls += " " + this.inputType + "-" + this.weight;
20946         }
20947         
20948         if (this.disabled) {
20949             input.disabled=true;
20950         }
20951         
20952         if(this.checked){
20953             input.checked = this.checked;
20954         }
20955         
20956         if (this.name) {
20957             
20958             input.name = this.name;
20959             
20960             if(this.inputType != 'radio'){
20961                 hidden.name = this.name;
20962                 input.name = '_hidden_' + this.name;
20963             }
20964         }
20965         
20966         if (this.size) {
20967             input.cls += ' input-' + this.size;
20968         }
20969         
20970         var settings=this;
20971         
20972         ['xs','sm','md','lg'].map(function(size){
20973             if (settings[size]) {
20974                 cfg.cls += ' col-' + size + '-' + settings[size];
20975             }
20976         });
20977         
20978         var inputblock = input;
20979          
20980         if (this.before || this.after) {
20981             
20982             inputblock = {
20983                 cls : 'input-group',
20984                 cn :  [] 
20985             };
20986             
20987             if (this.before) {
20988                 inputblock.cn.push({
20989                     tag :'span',
20990                     cls : 'input-group-addon',
20991                     html : this.before
20992                 });
20993             }
20994             
20995             inputblock.cn.push(input);
20996             
20997             if(this.inputType != 'radio'){
20998                 inputblock.cn.push(hidden);
20999             }
21000             
21001             if (this.after) {
21002                 inputblock.cn.push({
21003                     tag :'span',
21004                     cls : 'input-group-addon',
21005                     html : this.after
21006                 });
21007             }
21008             
21009         }
21010         var boxLabelCfg = false;
21011         
21012         if(this.boxLabel){
21013            
21014             boxLabelCfg = {
21015                 tag: 'label',
21016                 //'for': id, // box label is handled by onclick - so no for...
21017                 cls: 'box-label',
21018                 html: this.boxLabel
21019             };
21020             if(this.tooltip){
21021                 boxLabelCfg.tooltip = this.tooltip;
21022             }
21023              
21024         }
21025         
21026         
21027         if (align ==='left' && this.fieldLabel.length) {
21028 //                Roo.log("left and has label");
21029             cfg.cn = [
21030                 {
21031                     tag: 'label',
21032                     'for' :  id,
21033                     cls : 'control-label',
21034                     html : this.fieldLabel
21035                 },
21036                 {
21037                     cls : "", 
21038                     cn: [
21039                         inputblock
21040                     ]
21041                 }
21042             ];
21043             
21044             if (boxLabelCfg) {
21045                 cfg.cn[1].cn.push(boxLabelCfg);
21046             }
21047             
21048             if(this.labelWidth > 12){
21049                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
21050             }
21051             
21052             if(this.labelWidth < 13 && this.labelmd == 0){
21053                 this.labelmd = this.labelWidth;
21054             }
21055             
21056             if(this.labellg > 0){
21057                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21058                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21059             }
21060             
21061             if(this.labelmd > 0){
21062                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21063                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21064             }
21065             
21066             if(this.labelsm > 0){
21067                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21068                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21069             }
21070             
21071             if(this.labelxs > 0){
21072                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21073                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21074             }
21075             
21076         } else if ( this.fieldLabel.length) {
21077 //                Roo.log(" label");
21078                 cfg.cn = [
21079                    
21080                     {
21081                         tag: this.boxLabel ? 'span' : 'label',
21082                         'for': id,
21083                         cls: 'control-label box-input-label',
21084                         //cls : 'input-group-addon',
21085                         html : this.fieldLabel
21086                     },
21087                     
21088                     inputblock
21089                     
21090                 ];
21091                 if (boxLabelCfg) {
21092                     cfg.cn.push(boxLabelCfg);
21093                 }
21094
21095         } else {
21096             
21097 //                Roo.log(" no label && no align");
21098                 cfg.cn = [  inputblock ] ;
21099                 if (boxLabelCfg) {
21100                     cfg.cn.push(boxLabelCfg);
21101                 }
21102
21103                 
21104         }
21105         
21106        
21107         
21108         if(this.inputType != 'radio'){
21109             cfg.cn.push(hidden);
21110         }
21111         
21112         return cfg;
21113         
21114     },
21115     
21116     /**
21117      * return the real input element.
21118      */
21119     inputEl: function ()
21120     {
21121         return this.el.select('input.roo-' + this.inputType,true).first();
21122     },
21123     hiddenEl: function ()
21124     {
21125         return this.el.select('input.roo-hidden-value',true).first();
21126     },
21127     
21128     labelEl: function()
21129     {
21130         return this.el.select('label.control-label',true).first();
21131     },
21132     /* depricated... */
21133     
21134     label: function()
21135     {
21136         return this.labelEl();
21137     },
21138     
21139     boxLabelEl: function()
21140     {
21141         return this.el.select('label.box-label',true).first();
21142     },
21143     
21144     initEvents : function()
21145     {
21146 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21147         
21148         this.inputEl().on('click', this.onClick,  this);
21149         
21150         if (this.boxLabel) { 
21151             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21152         }
21153         
21154         this.startValue = this.getValue();
21155         
21156         if(this.groupId){
21157             Roo.bootstrap.CheckBox.register(this);
21158         }
21159     },
21160     
21161     onClick : function(e)
21162     {   
21163         if(this.fireEvent('click', this, e) !== false){
21164             this.setChecked(!this.checked);
21165         }
21166         
21167     },
21168     
21169     setChecked : function(state,suppressEvent)
21170     {
21171         this.startValue = this.getValue();
21172
21173         if(this.inputType == 'radio'){
21174             
21175             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21176                 e.dom.checked = false;
21177             });
21178             
21179             this.inputEl().dom.checked = true;
21180             
21181             this.inputEl().dom.value = this.inputValue;
21182             
21183             if(suppressEvent !== true){
21184                 this.fireEvent('check', this, true);
21185             }
21186             
21187             this.validate();
21188             
21189             return;
21190         }
21191         
21192         this.checked = state;
21193         
21194         this.inputEl().dom.checked = state;
21195         
21196         
21197         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21198         
21199         if(suppressEvent !== true){
21200             this.fireEvent('check', this, state);
21201         }
21202         
21203         this.validate();
21204     },
21205     
21206     getValue : function()
21207     {
21208         if(this.inputType == 'radio'){
21209             return this.getGroupValue();
21210         }
21211         
21212         return this.hiddenEl().dom.value;
21213         
21214     },
21215     
21216     getGroupValue : function()
21217     {
21218         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21219             return '';
21220         }
21221         
21222         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21223     },
21224     
21225     setValue : function(v,suppressEvent)
21226     {
21227         if(this.inputType == 'radio'){
21228             this.setGroupValue(v, suppressEvent);
21229             return;
21230         }
21231         
21232         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21233         
21234         this.validate();
21235     },
21236     
21237     setGroupValue : function(v, suppressEvent)
21238     {
21239         this.startValue = this.getValue();
21240         
21241         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21242             e.dom.checked = false;
21243             
21244             if(e.dom.value == v){
21245                 e.dom.checked = true;
21246             }
21247         });
21248         
21249         if(suppressEvent !== true){
21250             this.fireEvent('check', this, true);
21251         }
21252
21253         this.validate();
21254         
21255         return;
21256     },
21257     
21258     validate : function()
21259     {
21260         if(this.getVisibilityEl().hasClass('hidden')){
21261             return true;
21262         }
21263         
21264         if(
21265                 this.disabled || 
21266                 (this.inputType == 'radio' && this.validateRadio()) ||
21267                 (this.inputType == 'checkbox' && this.validateCheckbox())
21268         ){
21269             this.markValid();
21270             return true;
21271         }
21272         
21273         this.markInvalid();
21274         return false;
21275     },
21276     
21277     validateRadio : function()
21278     {
21279         if(this.getVisibilityEl().hasClass('hidden')){
21280             return true;
21281         }
21282         
21283         if(this.allowBlank){
21284             return true;
21285         }
21286         
21287         var valid = false;
21288         
21289         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21290             if(!e.dom.checked){
21291                 return;
21292             }
21293             
21294             valid = true;
21295             
21296             return false;
21297         });
21298         
21299         return valid;
21300     },
21301     
21302     validateCheckbox : function()
21303     {
21304         if(!this.groupId){
21305             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21306             //return (this.getValue() == this.inputValue) ? true : false;
21307         }
21308         
21309         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21310         
21311         if(!group){
21312             return false;
21313         }
21314         
21315         var r = false;
21316         
21317         for(var i in group){
21318             if(group[i].el.isVisible(true)){
21319                 r = false;
21320                 break;
21321             }
21322             
21323             r = true;
21324         }
21325         
21326         for(var i in group){
21327             if(r){
21328                 break;
21329             }
21330             
21331             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21332         }
21333         
21334         return r;
21335     },
21336     
21337     /**
21338      * Mark this field as valid
21339      */
21340     markValid : function()
21341     {
21342         var _this = this;
21343         
21344         this.fireEvent('valid', this);
21345         
21346         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21347         
21348         if(this.groupId){
21349             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21350         }
21351         
21352         if(label){
21353             label.markValid();
21354         }
21355
21356         if(this.inputType == 'radio'){
21357             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21358                 var fg = e.findParent('.form-group', false, true);
21359                 if (Roo.bootstrap.version == 3) {
21360                     fg.removeClass([_this.invalidClass, _this.validClass]);
21361                     fg.addClass(_this.validClass);
21362                 } else {
21363                     fg.removeClass(['is-valid', 'is-invalid']);
21364                     fg.addClass('is-valid');
21365                 }
21366             });
21367             
21368             return;
21369         }
21370
21371         if(!this.groupId){
21372             var fg = this.el.findParent('.form-group', false, true);
21373             if (Roo.bootstrap.version == 3) {
21374                 fg.removeClass([this.invalidClass, this.validClass]);
21375                 fg.addClass(this.validClass);
21376             } else {
21377                 fg.removeClass(['is-valid', 'is-invalid']);
21378                 fg.addClass('is-valid');
21379             }
21380             return;
21381         }
21382         
21383         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21384         
21385         if(!group){
21386             return;
21387         }
21388         
21389         for(var i in group){
21390             var fg = group[i].el.findParent('.form-group', false, true);
21391             if (Roo.bootstrap.version == 3) {
21392                 fg.removeClass([this.invalidClass, this.validClass]);
21393                 fg.addClass(this.validClass);
21394             } else {
21395                 fg.removeClass(['is-valid', 'is-invalid']);
21396                 fg.addClass('is-valid');
21397             }
21398         }
21399     },
21400     
21401      /**
21402      * Mark this field as invalid
21403      * @param {String} msg The validation message
21404      */
21405     markInvalid : function(msg)
21406     {
21407         if(this.allowBlank){
21408             return;
21409         }
21410         
21411         var _this = this;
21412         
21413         this.fireEvent('invalid', this, msg);
21414         
21415         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21416         
21417         if(this.groupId){
21418             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21419         }
21420         
21421         if(label){
21422             label.markInvalid();
21423         }
21424             
21425         if(this.inputType == 'radio'){
21426             
21427             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21428                 var fg = e.findParent('.form-group', false, true);
21429                 if (Roo.bootstrap.version == 3) {
21430                     fg.removeClass([_this.invalidClass, _this.validClass]);
21431                     fg.addClass(_this.invalidClass);
21432                 } else {
21433                     fg.removeClass(['is-invalid', 'is-valid']);
21434                     fg.addClass('is-invalid');
21435                 }
21436             });
21437             
21438             return;
21439         }
21440         
21441         if(!this.groupId){
21442             var fg = this.el.findParent('.form-group', false, true);
21443             if (Roo.bootstrap.version == 3) {
21444                 fg.removeClass([_this.invalidClass, _this.validClass]);
21445                 fg.addClass(_this.invalidClass);
21446             } else {
21447                 fg.removeClass(['is-invalid', 'is-valid']);
21448                 fg.addClass('is-invalid');
21449             }
21450             return;
21451         }
21452         
21453         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21454         
21455         if(!group){
21456             return;
21457         }
21458         
21459         for(var i in group){
21460             var fg = group[i].el.findParent('.form-group', false, true);
21461             if (Roo.bootstrap.version == 3) {
21462                 fg.removeClass([_this.invalidClass, _this.validClass]);
21463                 fg.addClass(_this.invalidClass);
21464             } else {
21465                 fg.removeClass(['is-invalid', 'is-valid']);
21466                 fg.addClass('is-invalid');
21467             }
21468         }
21469         
21470     },
21471     
21472     clearInvalid : function()
21473     {
21474         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21475         
21476         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21477         
21478         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21479         
21480         if (label && label.iconEl) {
21481             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21482             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21483         }
21484     },
21485     
21486     disable : function()
21487     {
21488         if(this.inputType != 'radio'){
21489             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21490             return;
21491         }
21492         
21493         var _this = this;
21494         
21495         if(this.rendered){
21496             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21497                 _this.getActionEl().addClass(this.disabledClass);
21498                 e.dom.disabled = true;
21499             });
21500         }
21501         
21502         this.disabled = true;
21503         this.fireEvent("disable", this);
21504         return this;
21505     },
21506
21507     enable : function()
21508     {
21509         if(this.inputType != 'radio'){
21510             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21511             return;
21512         }
21513         
21514         var _this = this;
21515         
21516         if(this.rendered){
21517             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21518                 _this.getActionEl().removeClass(this.disabledClass);
21519                 e.dom.disabled = false;
21520             });
21521         }
21522         
21523         this.disabled = false;
21524         this.fireEvent("enable", this);
21525         return this;
21526     },
21527     
21528     setBoxLabel : function(v)
21529     {
21530         this.boxLabel = v;
21531         
21532         if(this.rendered){
21533             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21534         }
21535     }
21536
21537 });
21538
21539 Roo.apply(Roo.bootstrap.CheckBox, {
21540     
21541     groups: {},
21542     
21543      /**
21544     * register a CheckBox Group
21545     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21546     */
21547     register : function(checkbox)
21548     {
21549         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21550             this.groups[checkbox.groupId] = {};
21551         }
21552         
21553         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21554             return;
21555         }
21556         
21557         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21558         
21559     },
21560     /**
21561     * fetch a CheckBox Group based on the group ID
21562     * @param {string} the group ID
21563     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21564     */
21565     get: function(groupId) {
21566         if (typeof(this.groups[groupId]) == 'undefined') {
21567             return false;
21568         }
21569         
21570         return this.groups[groupId] ;
21571     }
21572     
21573     
21574 });
21575 /*
21576  * - LGPL
21577  *
21578  * RadioItem
21579  * 
21580  */
21581
21582 /**
21583  * @class Roo.bootstrap.Radio
21584  * @extends Roo.bootstrap.Component
21585  * Bootstrap Radio class
21586  * @cfg {String} boxLabel - the label associated
21587  * @cfg {String} value - the value of radio
21588  * 
21589  * @constructor
21590  * Create a new Radio
21591  * @param {Object} config The config object
21592  */
21593 Roo.bootstrap.Radio = function(config){
21594     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21595     
21596 };
21597
21598 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21599     
21600     boxLabel : '',
21601     
21602     value : '',
21603     
21604     getAutoCreate : function()
21605     {
21606         var cfg = {
21607             tag : 'div',
21608             cls : 'form-group radio',
21609             cn : [
21610                 {
21611                     tag : 'label',
21612                     cls : 'box-label',
21613                     html : this.boxLabel
21614                 }
21615             ]
21616         };
21617         
21618         return cfg;
21619     },
21620     
21621     initEvents : function() 
21622     {
21623         this.parent().register(this);
21624         
21625         this.el.on('click', this.onClick, this);
21626         
21627     },
21628     
21629     onClick : function(e)
21630     {
21631         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21632             this.setChecked(true);
21633         }
21634     },
21635     
21636     setChecked : function(state, suppressEvent)
21637     {
21638         this.parent().setValue(this.value, suppressEvent);
21639         
21640     },
21641     
21642     setBoxLabel : function(v)
21643     {
21644         this.boxLabel = v;
21645         
21646         if(this.rendered){
21647             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21648         }
21649     }
21650     
21651 });
21652  
21653
21654  /*
21655  * - LGPL
21656  *
21657  * Input
21658  * 
21659  */
21660
21661 /**
21662  * @class Roo.bootstrap.SecurePass
21663  * @extends Roo.bootstrap.Input
21664  * Bootstrap SecurePass class
21665  *
21666  * 
21667  * @constructor
21668  * Create a new SecurePass
21669  * @param {Object} config The config object
21670  */
21671  
21672 Roo.bootstrap.SecurePass = function (config) {
21673     // these go here, so the translation tool can replace them..
21674     this.errors = {
21675         PwdEmpty: "Please type a password, and then retype it to confirm.",
21676         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21677         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21678         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21679         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21680         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21681         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21682         TooWeak: "Your password is Too Weak."
21683     },
21684     this.meterLabel = "Password strength:";
21685     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21686     this.meterClass = [
21687         "roo-password-meter-tooweak", 
21688         "roo-password-meter-weak", 
21689         "roo-password-meter-medium", 
21690         "roo-password-meter-strong", 
21691         "roo-password-meter-grey"
21692     ];
21693     
21694     this.errors = {};
21695     
21696     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21697 }
21698
21699 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21700     /**
21701      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21702      * {
21703      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21704      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21705      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21706      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21707      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21708      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21709      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21710      * })
21711      */
21712     // private
21713     
21714     meterWidth: 300,
21715     errorMsg :'',    
21716     errors: false,
21717     imageRoot: '/',
21718     /**
21719      * @cfg {String/Object} Label for the strength meter (defaults to
21720      * 'Password strength:')
21721      */
21722     // private
21723     meterLabel: '',
21724     /**
21725      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21726      * ['Weak', 'Medium', 'Strong'])
21727      */
21728     // private    
21729     pwdStrengths: false,    
21730     // private
21731     strength: 0,
21732     // private
21733     _lastPwd: null,
21734     // private
21735     kCapitalLetter: 0,
21736     kSmallLetter: 1,
21737     kDigit: 2,
21738     kPunctuation: 3,
21739     
21740     insecure: false,
21741     // private
21742     initEvents: function ()
21743     {
21744         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21745
21746         if (this.el.is('input[type=password]') && Roo.isSafari) {
21747             this.el.on('keydown', this.SafariOnKeyDown, this);
21748         }
21749
21750         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21751     },
21752     // private
21753     onRender: function (ct, position)
21754     {
21755         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21756         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21757         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21758
21759         this.trigger.createChild({
21760                    cn: [
21761                     {
21762                     //id: 'PwdMeter',
21763                     tag: 'div',
21764                     cls: 'roo-password-meter-grey col-xs-12',
21765                     style: {
21766                         //width: 0,
21767                         //width: this.meterWidth + 'px'                                                
21768                         }
21769                     },
21770                     {                            
21771                          cls: 'roo-password-meter-text'                          
21772                     }
21773                 ]            
21774         });
21775
21776          
21777         if (this.hideTrigger) {
21778             this.trigger.setDisplayed(false);
21779         }
21780         this.setSize(this.width || '', this.height || '');
21781     },
21782     // private
21783     onDestroy: function ()
21784     {
21785         if (this.trigger) {
21786             this.trigger.removeAllListeners();
21787             this.trigger.remove();
21788         }
21789         if (this.wrap) {
21790             this.wrap.remove();
21791         }
21792         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21793     },
21794     // private
21795     checkStrength: function ()
21796     {
21797         var pwd = this.inputEl().getValue();
21798         if (pwd == this._lastPwd) {
21799             return;
21800         }
21801
21802         var strength;
21803         if (this.ClientSideStrongPassword(pwd)) {
21804             strength = 3;
21805         } else if (this.ClientSideMediumPassword(pwd)) {
21806             strength = 2;
21807         } else if (this.ClientSideWeakPassword(pwd)) {
21808             strength = 1;
21809         } else {
21810             strength = 0;
21811         }
21812         
21813         Roo.log('strength1: ' + strength);
21814         
21815         //var pm = this.trigger.child('div/div/div').dom;
21816         var pm = this.trigger.child('div/div');
21817         pm.removeClass(this.meterClass);
21818         pm.addClass(this.meterClass[strength]);
21819                 
21820         
21821         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21822                 
21823         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21824         
21825         this._lastPwd = pwd;
21826     },
21827     reset: function ()
21828     {
21829         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21830         
21831         this._lastPwd = '';
21832         
21833         var pm = this.trigger.child('div/div');
21834         pm.removeClass(this.meterClass);
21835         pm.addClass('roo-password-meter-grey');        
21836         
21837         
21838         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21839         
21840         pt.innerHTML = '';
21841         this.inputEl().dom.type='password';
21842     },
21843     // private
21844     validateValue: function (value)
21845     {
21846         
21847         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21848             return false;
21849         }
21850         if (value.length == 0) {
21851             if (this.allowBlank) {
21852                 this.clearInvalid();
21853                 return true;
21854             }
21855
21856             this.markInvalid(this.errors.PwdEmpty);
21857             this.errorMsg = this.errors.PwdEmpty;
21858             return false;
21859         }
21860         
21861         if(this.insecure){
21862             return true;
21863         }
21864         
21865         if ('[\x21-\x7e]*'.match(value)) {
21866             this.markInvalid(this.errors.PwdBadChar);
21867             this.errorMsg = this.errors.PwdBadChar;
21868             return false;
21869         }
21870         if (value.length < 6) {
21871             this.markInvalid(this.errors.PwdShort);
21872             this.errorMsg = this.errors.PwdShort;
21873             return false;
21874         }
21875         if (value.length > 16) {
21876             this.markInvalid(this.errors.PwdLong);
21877             this.errorMsg = this.errors.PwdLong;
21878             return false;
21879         }
21880         var strength;
21881         if (this.ClientSideStrongPassword(value)) {
21882             strength = 3;
21883         } else if (this.ClientSideMediumPassword(value)) {
21884             strength = 2;
21885         } else if (this.ClientSideWeakPassword(value)) {
21886             strength = 1;
21887         } else {
21888             strength = 0;
21889         }
21890
21891         
21892         if (strength < 2) {
21893             //this.markInvalid(this.errors.TooWeak);
21894             this.errorMsg = this.errors.TooWeak;
21895             //return false;
21896         }
21897         
21898         
21899         console.log('strength2: ' + strength);
21900         
21901         //var pm = this.trigger.child('div/div/div').dom;
21902         
21903         var pm = this.trigger.child('div/div');
21904         pm.removeClass(this.meterClass);
21905         pm.addClass(this.meterClass[strength]);
21906                 
21907         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21908                 
21909         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21910         
21911         this.errorMsg = ''; 
21912         return true;
21913     },
21914     // private
21915     CharacterSetChecks: function (type)
21916     {
21917         this.type = type;
21918         this.fResult = false;
21919     },
21920     // private
21921     isctype: function (character, type)
21922     {
21923         switch (type) {  
21924             case this.kCapitalLetter:
21925                 if (character >= 'A' && character <= 'Z') {
21926                     return true;
21927                 }
21928                 break;
21929             
21930             case this.kSmallLetter:
21931                 if (character >= 'a' && character <= 'z') {
21932                     return true;
21933                 }
21934                 break;
21935             
21936             case this.kDigit:
21937                 if (character >= '0' && character <= '9') {
21938                     return true;
21939                 }
21940                 break;
21941             
21942             case this.kPunctuation:
21943                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21944                     return true;
21945                 }
21946                 break;
21947             
21948             default:
21949                 return false;
21950         }
21951
21952     },
21953     // private
21954     IsLongEnough: function (pwd, size)
21955     {
21956         return !(pwd == null || isNaN(size) || pwd.length < size);
21957     },
21958     // private
21959     SpansEnoughCharacterSets: function (word, nb)
21960     {
21961         if (!this.IsLongEnough(word, nb))
21962         {
21963             return false;
21964         }
21965
21966         var characterSetChecks = new Array(
21967             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21968             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21969         );
21970         
21971         for (var index = 0; index < word.length; ++index) {
21972             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21973                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21974                     characterSetChecks[nCharSet].fResult = true;
21975                     break;
21976                 }
21977             }
21978         }
21979
21980         var nCharSets = 0;
21981         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21982             if (characterSetChecks[nCharSet].fResult) {
21983                 ++nCharSets;
21984             }
21985         }
21986
21987         if (nCharSets < nb) {
21988             return false;
21989         }
21990         return true;
21991     },
21992     // private
21993     ClientSideStrongPassword: function (pwd)
21994     {
21995         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21996     },
21997     // private
21998     ClientSideMediumPassword: function (pwd)
21999     {
22000         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
22001     },
22002     // private
22003     ClientSideWeakPassword: function (pwd)
22004     {
22005         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
22006     }
22007           
22008 })//<script type="text/javascript">
22009
22010 /*
22011  * Based  Ext JS Library 1.1.1
22012  * Copyright(c) 2006-2007, Ext JS, LLC.
22013  * LGPL
22014  *
22015  */
22016  
22017 /**
22018  * @class Roo.HtmlEditorCore
22019  * @extends Roo.Component
22020  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
22021  *
22022  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22023  */
22024
22025 Roo.HtmlEditorCore = function(config){
22026     
22027     
22028     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
22029     
22030     
22031     this.addEvents({
22032         /**
22033          * @event initialize
22034          * Fires when the editor is fully initialized (including the iframe)
22035          * @param {Roo.HtmlEditorCore} this
22036          */
22037         initialize: true,
22038         /**
22039          * @event activate
22040          * Fires when the editor is first receives the focus. Any insertion must wait
22041          * until after this event.
22042          * @param {Roo.HtmlEditorCore} this
22043          */
22044         activate: true,
22045          /**
22046          * @event beforesync
22047          * Fires before the textarea is updated with content from the editor iframe. Return false
22048          * to cancel the sync.
22049          * @param {Roo.HtmlEditorCore} this
22050          * @param {String} html
22051          */
22052         beforesync: true,
22053          /**
22054          * @event beforepush
22055          * Fires before the iframe editor is updated with content from the textarea. Return false
22056          * to cancel the push.
22057          * @param {Roo.HtmlEditorCore} this
22058          * @param {String} html
22059          */
22060         beforepush: true,
22061          /**
22062          * @event sync
22063          * Fires when the textarea is updated with content from the editor iframe.
22064          * @param {Roo.HtmlEditorCore} this
22065          * @param {String} html
22066          */
22067         sync: true,
22068          /**
22069          * @event push
22070          * Fires when the iframe editor is updated with content from the textarea.
22071          * @param {Roo.HtmlEditorCore} this
22072          * @param {String} html
22073          */
22074         push: true,
22075         
22076         /**
22077          * @event editorevent
22078          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22079          * @param {Roo.HtmlEditorCore} this
22080          */
22081         editorevent: true
22082         
22083     });
22084     
22085     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22086     
22087     // defaults : white / black...
22088     this.applyBlacklists();
22089     
22090     
22091     
22092 };
22093
22094
22095 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22096
22097
22098      /**
22099      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22100      */
22101     
22102     owner : false,
22103     
22104      /**
22105      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22106      *                        Roo.resizable.
22107      */
22108     resizable : false,
22109      /**
22110      * @cfg {Number} height (in pixels)
22111      */   
22112     height: 300,
22113    /**
22114      * @cfg {Number} width (in pixels)
22115      */   
22116     width: 500,
22117     
22118     /**
22119      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22120      * 
22121      */
22122     stylesheets: false,
22123     
22124     // id of frame..
22125     frameId: false,
22126     
22127     // private properties
22128     validationEvent : false,
22129     deferHeight: true,
22130     initialized : false,
22131     activated : false,
22132     sourceEditMode : false,
22133     onFocus : Roo.emptyFn,
22134     iframePad:3,
22135     hideMode:'offsets',
22136     
22137     clearUp: true,
22138     
22139     // blacklist + whitelisted elements..
22140     black: false,
22141     white: false,
22142      
22143     bodyCls : '',
22144
22145     /**
22146      * Protected method that will not generally be called directly. It
22147      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22148      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22149      */
22150     getDocMarkup : function(){
22151         // body styles..
22152         var st = '';
22153         
22154         // inherit styels from page...?? 
22155         if (this.stylesheets === false) {
22156             
22157             Roo.get(document.head).select('style').each(function(node) {
22158                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22159             });
22160             
22161             Roo.get(document.head).select('link').each(function(node) { 
22162                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22163             });
22164             
22165         } else if (!this.stylesheets.length) {
22166                 // simple..
22167                 st = '<style type="text/css">' +
22168                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22169                    '</style>';
22170         } else { 
22171             st = '<style type="text/css">' +
22172                     this.stylesheets +
22173                 '</style>';
22174         }
22175         
22176         st +=  '<style type="text/css">' +
22177             'IMG { cursor: pointer } ' +
22178         '</style>';
22179
22180         var cls = 'roo-htmleditor-body';
22181         
22182         if(this.bodyCls.length){
22183             cls += ' ' + this.bodyCls;
22184         }
22185         
22186         return '<html><head>' + st  +
22187             //<style type="text/css">' +
22188             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22189             //'</style>' +
22190             ' </head><body class="' +  cls + '"></body></html>';
22191     },
22192
22193     // private
22194     onRender : function(ct, position)
22195     {
22196         var _t = this;
22197         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22198         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22199         
22200         
22201         this.el.dom.style.border = '0 none';
22202         this.el.dom.setAttribute('tabIndex', -1);
22203         this.el.addClass('x-hidden hide');
22204         
22205         
22206         
22207         if(Roo.isIE){ // fix IE 1px bogus margin
22208             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22209         }
22210        
22211         
22212         this.frameId = Roo.id();
22213         
22214          
22215         
22216         var iframe = this.owner.wrap.createChild({
22217             tag: 'iframe',
22218             cls: 'form-control', // bootstrap..
22219             id: this.frameId,
22220             name: this.frameId,
22221             frameBorder : 'no',
22222             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22223         }, this.el
22224         );
22225         
22226         
22227         this.iframe = iframe.dom;
22228
22229          this.assignDocWin();
22230         
22231         this.doc.designMode = 'on';
22232        
22233         this.doc.open();
22234         this.doc.write(this.getDocMarkup());
22235         this.doc.close();
22236
22237         
22238         var task = { // must defer to wait for browser to be ready
22239             run : function(){
22240                 //console.log("run task?" + this.doc.readyState);
22241                 this.assignDocWin();
22242                 if(this.doc.body || this.doc.readyState == 'complete'){
22243                     try {
22244                         this.doc.designMode="on";
22245                     } catch (e) {
22246                         return;
22247                     }
22248                     Roo.TaskMgr.stop(task);
22249                     this.initEditor.defer(10, this);
22250                 }
22251             },
22252             interval : 10,
22253             duration: 10000,
22254             scope: this
22255         };
22256         Roo.TaskMgr.start(task);
22257
22258     },
22259
22260     // private
22261     onResize : function(w, h)
22262     {
22263          Roo.log('resize: ' +w + ',' + h );
22264         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22265         if(!this.iframe){
22266             return;
22267         }
22268         if(typeof w == 'number'){
22269             
22270             this.iframe.style.width = w + 'px';
22271         }
22272         if(typeof h == 'number'){
22273             
22274             this.iframe.style.height = h + 'px';
22275             if(this.doc){
22276                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22277             }
22278         }
22279         
22280     },
22281
22282     /**
22283      * Toggles the editor between standard and source edit mode.
22284      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22285      */
22286     toggleSourceEdit : function(sourceEditMode){
22287         
22288         this.sourceEditMode = sourceEditMode === true;
22289         
22290         if(this.sourceEditMode){
22291  
22292             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22293             
22294         }else{
22295             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22296             //this.iframe.className = '';
22297             this.deferFocus();
22298         }
22299         //this.setSize(this.owner.wrap.getSize());
22300         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22301     },
22302
22303     
22304   
22305
22306     /**
22307      * Protected method that will not generally be called directly. If you need/want
22308      * custom HTML cleanup, this is the method you should override.
22309      * @param {String} html The HTML to be cleaned
22310      * return {String} The cleaned HTML
22311      */
22312     cleanHtml : function(html){
22313         html = String(html);
22314         if(html.length > 5){
22315             if(Roo.isSafari){ // strip safari nonsense
22316                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22317             }
22318         }
22319         if(html == '&nbsp;'){
22320             html = '';
22321         }
22322         return html;
22323     },
22324
22325     /**
22326      * HTML Editor -> Textarea
22327      * Protected method that will not generally be called directly. Syncs the contents
22328      * of the editor iframe with the textarea.
22329      */
22330     syncValue : function(){
22331         if(this.initialized){
22332             var bd = (this.doc.body || this.doc.documentElement);
22333             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22334             var html = bd.innerHTML;
22335             if(Roo.isSafari){
22336                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22337                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22338                 if(m && m[1]){
22339                     html = '<div style="'+m[0]+'">' + html + '</div>';
22340                 }
22341             }
22342             html = this.cleanHtml(html);
22343             // fix up the special chars.. normaly like back quotes in word...
22344             // however we do not want to do this with chinese..
22345             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
22346                 
22347                 var cc = match.charCodeAt();
22348
22349                 // Get the character value, handling surrogate pairs
22350                 if (match.length == 2) {
22351                     // It's a surrogate pair, calculate the Unicode code point
22352                     var high = match.charCodeAt(0) - 0xD800;
22353                     var low  = match.charCodeAt(1) - 0xDC00;
22354                     cc = (high * 0x400) + low + 0x10000;
22355                 }  else if (
22356                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22357                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22358                     (cc >= 0xf900 && cc < 0xfb00 )
22359                 ) {
22360                         return match;
22361                 }  
22362          
22363                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
22364                 return "&#" + cc + ";";
22365                 
22366                 
22367             });
22368             
22369             
22370              
22371             if(this.owner.fireEvent('beforesync', this, html) !== false){
22372                 this.el.dom.value = html;
22373                 this.owner.fireEvent('sync', this, html);
22374             }
22375         }
22376     },
22377
22378     /**
22379      * Protected method that will not generally be called directly. Pushes the value of the textarea
22380      * into the iframe editor.
22381      */
22382     pushValue : function(){
22383         if(this.initialized){
22384             var v = this.el.dom.value.trim();
22385             
22386 //            if(v.length < 1){
22387 //                v = '&#160;';
22388 //            }
22389             
22390             if(this.owner.fireEvent('beforepush', this, v) !== false){
22391                 var d = (this.doc.body || this.doc.documentElement);
22392                 d.innerHTML = v;
22393                 this.cleanUpPaste();
22394                 this.el.dom.value = d.innerHTML;
22395                 this.owner.fireEvent('push', this, v);
22396             }
22397         }
22398     },
22399
22400     // private
22401     deferFocus : function(){
22402         this.focus.defer(10, this);
22403     },
22404
22405     // doc'ed in Field
22406     focus : function(){
22407         if(this.win && !this.sourceEditMode){
22408             this.win.focus();
22409         }else{
22410             this.el.focus();
22411         }
22412     },
22413     
22414     assignDocWin: function()
22415     {
22416         var iframe = this.iframe;
22417         
22418          if(Roo.isIE){
22419             this.doc = iframe.contentWindow.document;
22420             this.win = iframe.contentWindow;
22421         } else {
22422 //            if (!Roo.get(this.frameId)) {
22423 //                return;
22424 //            }
22425 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22426 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22427             
22428             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22429                 return;
22430             }
22431             
22432             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22433             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22434         }
22435     },
22436     
22437     // private
22438     initEditor : function(){
22439         //console.log("INIT EDITOR");
22440         this.assignDocWin();
22441         
22442         
22443         
22444         this.doc.designMode="on";
22445         this.doc.open();
22446         this.doc.write(this.getDocMarkup());
22447         this.doc.close();
22448         
22449         var dbody = (this.doc.body || this.doc.documentElement);
22450         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22451         // this copies styles from the containing element into thsi one..
22452         // not sure why we need all of this..
22453         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22454         
22455         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22456         //ss['background-attachment'] = 'fixed'; // w3c
22457         dbody.bgProperties = 'fixed'; // ie
22458         //Roo.DomHelper.applyStyles(dbody, ss);
22459         Roo.EventManager.on(this.doc, {
22460             //'mousedown': this.onEditorEvent,
22461             'mouseup': this.onEditorEvent,
22462             'dblclick': this.onEditorEvent,
22463             'click': this.onEditorEvent,
22464             'keyup': this.onEditorEvent,
22465             buffer:100,
22466             scope: this
22467         });
22468         if(Roo.isGecko){
22469             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22470         }
22471         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22472             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22473         }
22474         this.initialized = true;
22475
22476         this.owner.fireEvent('initialize', this);
22477         this.pushValue();
22478     },
22479
22480     // private
22481     onDestroy : function(){
22482         
22483         
22484         
22485         if(this.rendered){
22486             
22487             //for (var i =0; i < this.toolbars.length;i++) {
22488             //    // fixme - ask toolbars for heights?
22489             //    this.toolbars[i].onDestroy();
22490            // }
22491             
22492             //this.wrap.dom.innerHTML = '';
22493             //this.wrap.remove();
22494         }
22495     },
22496
22497     // private
22498     onFirstFocus : function(){
22499         
22500         this.assignDocWin();
22501         
22502         
22503         this.activated = true;
22504          
22505     
22506         if(Roo.isGecko){ // prevent silly gecko errors
22507             this.win.focus();
22508             var s = this.win.getSelection();
22509             if(!s.focusNode || s.focusNode.nodeType != 3){
22510                 var r = s.getRangeAt(0);
22511                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22512                 r.collapse(true);
22513                 this.deferFocus();
22514             }
22515             try{
22516                 this.execCmd('useCSS', true);
22517                 this.execCmd('styleWithCSS', false);
22518             }catch(e){}
22519         }
22520         this.owner.fireEvent('activate', this);
22521     },
22522
22523     // private
22524     adjustFont: function(btn){
22525         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22526         //if(Roo.isSafari){ // safari
22527         //    adjust *= 2;
22528        // }
22529         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22530         if(Roo.isSafari){ // safari
22531             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22532             v =  (v < 10) ? 10 : v;
22533             v =  (v > 48) ? 48 : v;
22534             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22535             
22536         }
22537         
22538         
22539         v = Math.max(1, v+adjust);
22540         
22541         this.execCmd('FontSize', v  );
22542     },
22543
22544     onEditorEvent : function(e)
22545     {
22546         this.owner.fireEvent('editorevent', this, e);
22547       //  this.updateToolbar();
22548         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22549     },
22550
22551     insertTag : function(tg)
22552     {
22553         // could be a bit smarter... -> wrap the current selected tRoo..
22554         if (tg.toLowerCase() == 'span' ||
22555             tg.toLowerCase() == 'code' ||
22556             tg.toLowerCase() == 'sup' ||
22557             tg.toLowerCase() == 'sub' 
22558             ) {
22559             
22560             range = this.createRange(this.getSelection());
22561             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22562             wrappingNode.appendChild(range.extractContents());
22563             range.insertNode(wrappingNode);
22564
22565             return;
22566             
22567             
22568             
22569         }
22570         this.execCmd("formatblock",   tg);
22571         
22572     },
22573     
22574     insertText : function(txt)
22575     {
22576         
22577         
22578         var range = this.createRange();
22579         range.deleteContents();
22580                //alert(Sender.getAttribute('label'));
22581                
22582         range.insertNode(this.doc.createTextNode(txt));
22583     } ,
22584     
22585      
22586
22587     /**
22588      * Executes a Midas editor command on the editor document and performs necessary focus and
22589      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22590      * @param {String} cmd The Midas command
22591      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22592      */
22593     relayCmd : function(cmd, value){
22594         this.win.focus();
22595         this.execCmd(cmd, value);
22596         this.owner.fireEvent('editorevent', this);
22597         //this.updateToolbar();
22598         this.owner.deferFocus();
22599     },
22600
22601     /**
22602      * Executes a Midas editor command directly on the editor document.
22603      * For visual commands, you should use {@link #relayCmd} instead.
22604      * <b>This should only be called after the editor is initialized.</b>
22605      * @param {String} cmd The Midas command
22606      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22607      */
22608     execCmd : function(cmd, value){
22609         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22610         this.syncValue();
22611     },
22612  
22613  
22614    
22615     /**
22616      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22617      * to insert tRoo.
22618      * @param {String} text | dom node.. 
22619      */
22620     insertAtCursor : function(text)
22621     {
22622         
22623         if(!this.activated){
22624             return;
22625         }
22626         /*
22627         if(Roo.isIE){
22628             this.win.focus();
22629             var r = this.doc.selection.createRange();
22630             if(r){
22631                 r.collapse(true);
22632                 r.pasteHTML(text);
22633                 this.syncValue();
22634                 this.deferFocus();
22635             
22636             }
22637             return;
22638         }
22639         */
22640         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22641             this.win.focus();
22642             
22643             
22644             // from jquery ui (MIT licenced)
22645             var range, node;
22646             var win = this.win;
22647             
22648             if (win.getSelection && win.getSelection().getRangeAt) {
22649                 range = win.getSelection().getRangeAt(0);
22650                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22651                 range.insertNode(node);
22652             } else if (win.document.selection && win.document.selection.createRange) {
22653                 // no firefox support
22654                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22655                 win.document.selection.createRange().pasteHTML(txt);
22656             } else {
22657                 // no firefox support
22658                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22659                 this.execCmd('InsertHTML', txt);
22660             } 
22661             
22662             this.syncValue();
22663             
22664             this.deferFocus();
22665         }
22666     },
22667  // private
22668     mozKeyPress : function(e){
22669         if(e.ctrlKey){
22670             var c = e.getCharCode(), cmd;
22671           
22672             if(c > 0){
22673                 c = String.fromCharCode(c).toLowerCase();
22674                 switch(c){
22675                     case 'b':
22676                         cmd = 'bold';
22677                         break;
22678                     case 'i':
22679                         cmd = 'italic';
22680                         break;
22681                     
22682                     case 'u':
22683                         cmd = 'underline';
22684                         break;
22685                     
22686                     case 'v':
22687                         this.cleanUpPaste.defer(100, this);
22688                         return;
22689                         
22690                 }
22691                 if(cmd){
22692                     this.win.focus();
22693                     this.execCmd(cmd);
22694                     this.deferFocus();
22695                     e.preventDefault();
22696                 }
22697                 
22698             }
22699         }
22700     },
22701
22702     // private
22703     fixKeys : function(){ // load time branching for fastest keydown performance
22704         if(Roo.isIE){
22705             return function(e){
22706                 var k = e.getKey(), r;
22707                 if(k == e.TAB){
22708                     e.stopEvent();
22709                     r = this.doc.selection.createRange();
22710                     if(r){
22711                         r.collapse(true);
22712                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22713                         this.deferFocus();
22714                     }
22715                     return;
22716                 }
22717                 
22718                 if(k == e.ENTER){
22719                     r = this.doc.selection.createRange();
22720                     if(r){
22721                         var target = r.parentElement();
22722                         if(!target || target.tagName.toLowerCase() != 'li'){
22723                             e.stopEvent();
22724                             r.pasteHTML('<br />');
22725                             r.collapse(false);
22726                             r.select();
22727                         }
22728                     }
22729                 }
22730                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22731                     this.cleanUpPaste.defer(100, this);
22732                     return;
22733                 }
22734                 
22735                 
22736             };
22737         }else if(Roo.isOpera){
22738             return function(e){
22739                 var k = e.getKey();
22740                 if(k == e.TAB){
22741                     e.stopEvent();
22742                     this.win.focus();
22743                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22744                     this.deferFocus();
22745                 }
22746                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22747                     this.cleanUpPaste.defer(100, this);
22748                     return;
22749                 }
22750                 
22751             };
22752         }else if(Roo.isSafari){
22753             return function(e){
22754                 var k = e.getKey();
22755                 
22756                 if(k == e.TAB){
22757                     e.stopEvent();
22758                     this.execCmd('InsertText','\t');
22759                     this.deferFocus();
22760                     return;
22761                 }
22762                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22763                     this.cleanUpPaste.defer(100, this);
22764                     return;
22765                 }
22766                 
22767              };
22768         }
22769     }(),
22770     
22771     getAllAncestors: function()
22772     {
22773         var p = this.getSelectedNode();
22774         var a = [];
22775         if (!p) {
22776             a.push(p); // push blank onto stack..
22777             p = this.getParentElement();
22778         }
22779         
22780         
22781         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22782             a.push(p);
22783             p = p.parentNode;
22784         }
22785         a.push(this.doc.body);
22786         return a;
22787     },
22788     lastSel : false,
22789     lastSelNode : false,
22790     
22791     
22792     getSelection : function() 
22793     {
22794         this.assignDocWin();
22795         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22796     },
22797     
22798     getSelectedNode: function() 
22799     {
22800         // this may only work on Gecko!!!
22801         
22802         // should we cache this!!!!
22803         
22804         
22805         
22806          
22807         var range = this.createRange(this.getSelection()).cloneRange();
22808         
22809         if (Roo.isIE) {
22810             var parent = range.parentElement();
22811             while (true) {
22812                 var testRange = range.duplicate();
22813                 testRange.moveToElementText(parent);
22814                 if (testRange.inRange(range)) {
22815                     break;
22816                 }
22817                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22818                     break;
22819                 }
22820                 parent = parent.parentElement;
22821             }
22822             return parent;
22823         }
22824         
22825         // is ancestor a text element.
22826         var ac =  range.commonAncestorContainer;
22827         if (ac.nodeType == 3) {
22828             ac = ac.parentNode;
22829         }
22830         
22831         var ar = ac.childNodes;
22832          
22833         var nodes = [];
22834         var other_nodes = [];
22835         var has_other_nodes = false;
22836         for (var i=0;i<ar.length;i++) {
22837             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22838                 continue;
22839             }
22840             // fullly contained node.
22841             
22842             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22843                 nodes.push(ar[i]);
22844                 continue;
22845             }
22846             
22847             // probably selected..
22848             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22849                 other_nodes.push(ar[i]);
22850                 continue;
22851             }
22852             // outer..
22853             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22854                 continue;
22855             }
22856             
22857             
22858             has_other_nodes = true;
22859         }
22860         if (!nodes.length && other_nodes.length) {
22861             nodes= other_nodes;
22862         }
22863         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22864             return false;
22865         }
22866         
22867         return nodes[0];
22868     },
22869     createRange: function(sel)
22870     {
22871         // this has strange effects when using with 
22872         // top toolbar - not sure if it's a great idea.
22873         //this.editor.contentWindow.focus();
22874         if (typeof sel != "undefined") {
22875             try {
22876                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22877             } catch(e) {
22878                 return this.doc.createRange();
22879             }
22880         } else {
22881             return this.doc.createRange();
22882         }
22883     },
22884     getParentElement: function()
22885     {
22886         
22887         this.assignDocWin();
22888         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22889         
22890         var range = this.createRange(sel);
22891          
22892         try {
22893             var p = range.commonAncestorContainer;
22894             while (p.nodeType == 3) { // text node
22895                 p = p.parentNode;
22896             }
22897             return p;
22898         } catch (e) {
22899             return null;
22900         }
22901     
22902     },
22903     /***
22904      *
22905      * Range intersection.. the hard stuff...
22906      *  '-1' = before
22907      *  '0' = hits..
22908      *  '1' = after.
22909      *         [ -- selected range --- ]
22910      *   [fail]                        [fail]
22911      *
22912      *    basically..
22913      *      if end is before start or  hits it. fail.
22914      *      if start is after end or hits it fail.
22915      *
22916      *   if either hits (but other is outside. - then it's not 
22917      *   
22918      *    
22919      **/
22920     
22921     
22922     // @see http://www.thismuchiknow.co.uk/?p=64.
22923     rangeIntersectsNode : function(range, node)
22924     {
22925         var nodeRange = node.ownerDocument.createRange();
22926         try {
22927             nodeRange.selectNode(node);
22928         } catch (e) {
22929             nodeRange.selectNodeContents(node);
22930         }
22931     
22932         var rangeStartRange = range.cloneRange();
22933         rangeStartRange.collapse(true);
22934     
22935         var rangeEndRange = range.cloneRange();
22936         rangeEndRange.collapse(false);
22937     
22938         var nodeStartRange = nodeRange.cloneRange();
22939         nodeStartRange.collapse(true);
22940     
22941         var nodeEndRange = nodeRange.cloneRange();
22942         nodeEndRange.collapse(false);
22943     
22944         return rangeStartRange.compareBoundaryPoints(
22945                  Range.START_TO_START, nodeEndRange) == -1 &&
22946                rangeEndRange.compareBoundaryPoints(
22947                  Range.START_TO_START, nodeStartRange) == 1;
22948         
22949          
22950     },
22951     rangeCompareNode : function(range, node)
22952     {
22953         var nodeRange = node.ownerDocument.createRange();
22954         try {
22955             nodeRange.selectNode(node);
22956         } catch (e) {
22957             nodeRange.selectNodeContents(node);
22958         }
22959         
22960         
22961         range.collapse(true);
22962     
22963         nodeRange.collapse(true);
22964      
22965         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22966         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22967          
22968         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22969         
22970         var nodeIsBefore   =  ss == 1;
22971         var nodeIsAfter    = ee == -1;
22972         
22973         if (nodeIsBefore && nodeIsAfter) {
22974             return 0; // outer
22975         }
22976         if (!nodeIsBefore && nodeIsAfter) {
22977             return 1; //right trailed.
22978         }
22979         
22980         if (nodeIsBefore && !nodeIsAfter) {
22981             return 2;  // left trailed.
22982         }
22983         // fully contined.
22984         return 3;
22985     },
22986
22987     // private? - in a new class?
22988     cleanUpPaste :  function()
22989     {
22990         // cleans up the whole document..
22991         Roo.log('cleanuppaste');
22992         
22993         this.cleanUpChildren(this.doc.body);
22994         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22995         if (clean != this.doc.body.innerHTML) {
22996             this.doc.body.innerHTML = clean;
22997         }
22998         
22999     },
23000     
23001     cleanWordChars : function(input) {// change the chars to hex code
23002         var he = Roo.HtmlEditorCore;
23003         
23004         var output = input;
23005         Roo.each(he.swapCodes, function(sw) { 
23006             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
23007             
23008             output = output.replace(swapper, sw[1]);
23009         });
23010         
23011         return output;
23012     },
23013     
23014     
23015     cleanUpChildren : function (n)
23016     {
23017         if (!n.childNodes.length) {
23018             return;
23019         }
23020         for (var i = n.childNodes.length-1; i > -1 ; i--) {
23021            this.cleanUpChild(n.childNodes[i]);
23022         }
23023     },
23024     
23025     
23026         
23027     
23028     cleanUpChild : function (node)
23029     {
23030         var ed = this;
23031         //console.log(node);
23032         if (node.nodeName == "#text") {
23033             // clean up silly Windows -- stuff?
23034             return; 
23035         }
23036         if (node.nodeName == "#comment") {
23037             node.parentNode.removeChild(node);
23038             // clean up silly Windows -- stuff?
23039             return; 
23040         }
23041         var lcname = node.tagName.toLowerCase();
23042         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
23043         // whitelist of tags..
23044         
23045         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
23046             // remove node.
23047             node.parentNode.removeChild(node);
23048             return;
23049             
23050         }
23051         
23052         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
23053         
23054         // spans with no attributes - just remove them..
23055         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
23056             remove_keep_children = true;
23057         }
23058         
23059         // remove <a name=....> as rendering on yahoo mailer is borked with this.
23060         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
23061         
23062         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
23063         //    remove_keep_children = true;
23064         //}
23065         
23066         if (remove_keep_children) {
23067             this.cleanUpChildren(node);
23068             // inserts everything just before this node...
23069             while (node.childNodes.length) {
23070                 var cn = node.childNodes[0];
23071                 node.removeChild(cn);
23072                 node.parentNode.insertBefore(cn, node);
23073             }
23074             node.parentNode.removeChild(node);
23075             return;
23076         }
23077         
23078         if (!node.attributes || !node.attributes.length) {
23079             
23080           
23081             
23082             
23083             this.cleanUpChildren(node);
23084             return;
23085         }
23086         
23087         function cleanAttr(n,v)
23088         {
23089             
23090             if (v.match(/^\./) || v.match(/^\//)) {
23091                 return;
23092             }
23093             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23094                 return;
23095             }
23096             if (v.match(/^#/)) {
23097                 return;
23098             }
23099 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23100             node.removeAttribute(n);
23101             
23102         }
23103         
23104         var cwhite = this.cwhite;
23105         var cblack = this.cblack;
23106             
23107         function cleanStyle(n,v)
23108         {
23109             if (v.match(/expression/)) { //XSS?? should we even bother..
23110                 node.removeAttribute(n);
23111                 return;
23112             }
23113             
23114             var parts = v.split(/;/);
23115             var clean = [];
23116             
23117             Roo.each(parts, function(p) {
23118                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23119                 if (!p.length) {
23120                     return true;
23121                 }
23122                 var l = p.split(':').shift().replace(/\s+/g,'');
23123                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23124                 
23125                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23126 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23127                     //node.removeAttribute(n);
23128                     return true;
23129                 }
23130                 //Roo.log()
23131                 // only allow 'c whitelisted system attributes'
23132                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23133 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23134                     //node.removeAttribute(n);
23135                     return true;
23136                 }
23137                 
23138                 
23139                  
23140                 
23141                 clean.push(p);
23142                 return true;
23143             });
23144             if (clean.length) { 
23145                 node.setAttribute(n, clean.join(';'));
23146             } else {
23147                 node.removeAttribute(n);
23148             }
23149             
23150         }
23151         
23152         
23153         for (var i = node.attributes.length-1; i > -1 ; i--) {
23154             var a = node.attributes[i];
23155             //console.log(a);
23156             
23157             if (a.name.toLowerCase().substr(0,2)=='on')  {
23158                 node.removeAttribute(a.name);
23159                 continue;
23160             }
23161             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23162                 node.removeAttribute(a.name);
23163                 continue;
23164             }
23165             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23166                 cleanAttr(a.name,a.value); // fixme..
23167                 continue;
23168             }
23169             if (a.name == 'style') {
23170                 cleanStyle(a.name,a.value);
23171                 continue;
23172             }
23173             /// clean up MS crap..
23174             // tecnically this should be a list of valid class'es..
23175             
23176             
23177             if (a.name == 'class') {
23178                 if (a.value.match(/^Mso/)) {
23179                     node.removeAttribute('class');
23180                 }
23181                 
23182                 if (a.value.match(/^body$/)) {
23183                     node.removeAttribute('class');
23184                 }
23185                 continue;
23186             }
23187             
23188             // style cleanup!?
23189             // class cleanup?
23190             
23191         }
23192         
23193         
23194         this.cleanUpChildren(node);
23195         
23196         
23197     },
23198     
23199     /**
23200      * Clean up MS wordisms...
23201      */
23202     cleanWord : function(node)
23203     {
23204         if (!node) {
23205             this.cleanWord(this.doc.body);
23206             return;
23207         }
23208         
23209         if(
23210                 node.nodeName == 'SPAN' &&
23211                 !node.hasAttributes() &&
23212                 node.childNodes.length == 1 &&
23213                 node.firstChild.nodeName == "#text"  
23214         ) {
23215             var textNode = node.firstChild;
23216             node.removeChild(textNode);
23217             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
23218                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
23219             }
23220             node.parentNode.insertBefore(textNode, node);
23221             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
23222                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
23223             }
23224             node.parentNode.removeChild(node);
23225         }
23226         
23227         if (node.nodeName == "#text") {
23228             // clean up silly Windows -- stuff?
23229             return; 
23230         }
23231         if (node.nodeName == "#comment") {
23232             node.parentNode.removeChild(node);
23233             // clean up silly Windows -- stuff?
23234             return; 
23235         }
23236         
23237         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23238             node.parentNode.removeChild(node);
23239             return;
23240         }
23241         //Roo.log(node.tagName);
23242         // remove - but keep children..
23243         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
23244             //Roo.log('-- removed');
23245             while (node.childNodes.length) {
23246                 var cn = node.childNodes[0];
23247                 node.removeChild(cn);
23248                 node.parentNode.insertBefore(cn, node);
23249                 // move node to parent - and clean it..
23250                 this.cleanWord(cn);
23251             }
23252             node.parentNode.removeChild(node);
23253             /// no need to iterate chidlren = it's got none..
23254             //this.iterateChildren(node, this.cleanWord);
23255             return;
23256         }
23257         // clean styles
23258         if (node.className.length) {
23259             
23260             var cn = node.className.split(/\W+/);
23261             var cna = [];
23262             Roo.each(cn, function(cls) {
23263                 if (cls.match(/Mso[a-zA-Z]+/)) {
23264                     return;
23265                 }
23266                 cna.push(cls);
23267             });
23268             node.className = cna.length ? cna.join(' ') : '';
23269             if (!cna.length) {
23270                 node.removeAttribute("class");
23271             }
23272         }
23273         
23274         if (node.hasAttribute("lang")) {
23275             node.removeAttribute("lang");
23276         }
23277         
23278         if (node.hasAttribute("style")) {
23279             
23280             var styles = node.getAttribute("style").split(";");
23281             var nstyle = [];
23282             Roo.each(styles, function(s) {
23283                 if (!s.match(/:/)) {
23284                     return;
23285                 }
23286                 var kv = s.split(":");
23287                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23288                     return;
23289                 }
23290                 // what ever is left... we allow.
23291                 nstyle.push(s);
23292             });
23293             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23294             if (!nstyle.length) {
23295                 node.removeAttribute('style');
23296             }
23297         }
23298         this.iterateChildren(node, this.cleanWord);
23299         
23300         
23301         
23302     },
23303     /**
23304      * iterateChildren of a Node, calling fn each time, using this as the scole..
23305      * @param {DomNode} node node to iterate children of.
23306      * @param {Function} fn method of this class to call on each item.
23307      */
23308     iterateChildren : function(node, fn)
23309     {
23310         if (!node.childNodes.length) {
23311                 return;
23312         }
23313         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23314            fn.call(this, node.childNodes[i])
23315         }
23316     },
23317     
23318     
23319     /**
23320      * cleanTableWidths.
23321      *
23322      * Quite often pasting from word etc.. results in tables with column and widths.
23323      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23324      *
23325      */
23326     cleanTableWidths : function(node)
23327     {
23328          
23329          
23330         if (!node) {
23331             this.cleanTableWidths(this.doc.body);
23332             return;
23333         }
23334         
23335         // ignore list...
23336         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23337             return; 
23338         }
23339         Roo.log(node.tagName);
23340         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23341             this.iterateChildren(node, this.cleanTableWidths);
23342             return;
23343         }
23344         if (node.hasAttribute('width')) {
23345             node.removeAttribute('width');
23346         }
23347         
23348          
23349         if (node.hasAttribute("style")) {
23350             // pretty basic...
23351             
23352             var styles = node.getAttribute("style").split(";");
23353             var nstyle = [];
23354             Roo.each(styles, function(s) {
23355                 if (!s.match(/:/)) {
23356                     return;
23357                 }
23358                 var kv = s.split(":");
23359                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23360                     return;
23361                 }
23362                 // what ever is left... we allow.
23363                 nstyle.push(s);
23364             });
23365             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23366             if (!nstyle.length) {
23367                 node.removeAttribute('style');
23368             }
23369         }
23370         
23371         this.iterateChildren(node, this.cleanTableWidths);
23372         
23373         
23374     },
23375     
23376     
23377     
23378     
23379     domToHTML : function(currentElement, depth, nopadtext) {
23380         
23381         depth = depth || 0;
23382         nopadtext = nopadtext || false;
23383     
23384         if (!currentElement) {
23385             return this.domToHTML(this.doc.body);
23386         }
23387         
23388         //Roo.log(currentElement);
23389         var j;
23390         var allText = false;
23391         var nodeName = currentElement.nodeName;
23392         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23393         
23394         if  (nodeName == '#text') {
23395             
23396             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23397         }
23398         
23399         
23400         var ret = '';
23401         if (nodeName != 'BODY') {
23402              
23403             var i = 0;
23404             // Prints the node tagName, such as <A>, <IMG>, etc
23405             if (tagName) {
23406                 var attr = [];
23407                 for(i = 0; i < currentElement.attributes.length;i++) {
23408                     // quoting?
23409                     var aname = currentElement.attributes.item(i).name;
23410                     if (!currentElement.attributes.item(i).value.length) {
23411                         continue;
23412                     }
23413                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23414                 }
23415                 
23416                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23417             } 
23418             else {
23419                 
23420                 // eack
23421             }
23422         } else {
23423             tagName = false;
23424         }
23425         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23426             return ret;
23427         }
23428         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23429             nopadtext = true;
23430         }
23431         
23432         
23433         // Traverse the tree
23434         i = 0;
23435         var currentElementChild = currentElement.childNodes.item(i);
23436         var allText = true;
23437         var innerHTML  = '';
23438         lastnode = '';
23439         while (currentElementChild) {
23440             // Formatting code (indent the tree so it looks nice on the screen)
23441             var nopad = nopadtext;
23442             if (lastnode == 'SPAN') {
23443                 nopad  = true;
23444             }
23445             // text
23446             if  (currentElementChild.nodeName == '#text') {
23447                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23448                 toadd = nopadtext ? toadd : toadd.trim();
23449                 if (!nopad && toadd.length > 80) {
23450                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23451                 }
23452                 innerHTML  += toadd;
23453                 
23454                 i++;
23455                 currentElementChild = currentElement.childNodes.item(i);
23456                 lastNode = '';
23457                 continue;
23458             }
23459             allText = false;
23460             
23461             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23462                 
23463             // Recursively traverse the tree structure of the child node
23464             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23465             lastnode = currentElementChild.nodeName;
23466             i++;
23467             currentElementChild=currentElement.childNodes.item(i);
23468         }
23469         
23470         ret += innerHTML;
23471         
23472         if (!allText) {
23473                 // The remaining code is mostly for formatting the tree
23474             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23475         }
23476         
23477         
23478         if (tagName) {
23479             ret+= "</"+tagName+">";
23480         }
23481         return ret;
23482         
23483     },
23484         
23485     applyBlacklists : function()
23486     {
23487         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23488         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23489         
23490         this.white = [];
23491         this.black = [];
23492         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23493             if (b.indexOf(tag) > -1) {
23494                 return;
23495             }
23496             this.white.push(tag);
23497             
23498         }, this);
23499         
23500         Roo.each(w, function(tag) {
23501             if (b.indexOf(tag) > -1) {
23502                 return;
23503             }
23504             if (this.white.indexOf(tag) > -1) {
23505                 return;
23506             }
23507             this.white.push(tag);
23508             
23509         }, this);
23510         
23511         
23512         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23513             if (w.indexOf(tag) > -1) {
23514                 return;
23515             }
23516             this.black.push(tag);
23517             
23518         }, this);
23519         
23520         Roo.each(b, function(tag) {
23521             if (w.indexOf(tag) > -1) {
23522                 return;
23523             }
23524             if (this.black.indexOf(tag) > -1) {
23525                 return;
23526             }
23527             this.black.push(tag);
23528             
23529         }, this);
23530         
23531         
23532         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23533         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23534         
23535         this.cwhite = [];
23536         this.cblack = [];
23537         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23538             if (b.indexOf(tag) > -1) {
23539                 return;
23540             }
23541             this.cwhite.push(tag);
23542             
23543         }, this);
23544         
23545         Roo.each(w, function(tag) {
23546             if (b.indexOf(tag) > -1) {
23547                 return;
23548             }
23549             if (this.cwhite.indexOf(tag) > -1) {
23550                 return;
23551             }
23552             this.cwhite.push(tag);
23553             
23554         }, this);
23555         
23556         
23557         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23558             if (w.indexOf(tag) > -1) {
23559                 return;
23560             }
23561             this.cblack.push(tag);
23562             
23563         }, this);
23564         
23565         Roo.each(b, function(tag) {
23566             if (w.indexOf(tag) > -1) {
23567                 return;
23568             }
23569             if (this.cblack.indexOf(tag) > -1) {
23570                 return;
23571             }
23572             this.cblack.push(tag);
23573             
23574         }, this);
23575     },
23576     
23577     setStylesheets : function(stylesheets)
23578     {
23579         if(typeof(stylesheets) == 'string'){
23580             Roo.get(this.iframe.contentDocument.head).createChild({
23581                 tag : 'link',
23582                 rel : 'stylesheet',
23583                 type : 'text/css',
23584                 href : stylesheets
23585             });
23586             
23587             return;
23588         }
23589         var _this = this;
23590      
23591         Roo.each(stylesheets, function(s) {
23592             if(!s.length){
23593                 return;
23594             }
23595             
23596             Roo.get(_this.iframe.contentDocument.head).createChild({
23597                 tag : 'link',
23598                 rel : 'stylesheet',
23599                 type : 'text/css',
23600                 href : s
23601             });
23602         });
23603
23604         
23605     },
23606     
23607     removeStylesheets : function()
23608     {
23609         var _this = this;
23610         
23611         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23612             s.remove();
23613         });
23614     },
23615     
23616     setStyle : function(style)
23617     {
23618         Roo.get(this.iframe.contentDocument.head).createChild({
23619             tag : 'style',
23620             type : 'text/css',
23621             html : style
23622         });
23623
23624         return;
23625     }
23626     
23627     // hide stuff that is not compatible
23628     /**
23629      * @event blur
23630      * @hide
23631      */
23632     /**
23633      * @event change
23634      * @hide
23635      */
23636     /**
23637      * @event focus
23638      * @hide
23639      */
23640     /**
23641      * @event specialkey
23642      * @hide
23643      */
23644     /**
23645      * @cfg {String} fieldClass @hide
23646      */
23647     /**
23648      * @cfg {String} focusClass @hide
23649      */
23650     /**
23651      * @cfg {String} autoCreate @hide
23652      */
23653     /**
23654      * @cfg {String} inputType @hide
23655      */
23656     /**
23657      * @cfg {String} invalidClass @hide
23658      */
23659     /**
23660      * @cfg {String} invalidText @hide
23661      */
23662     /**
23663      * @cfg {String} msgFx @hide
23664      */
23665     /**
23666      * @cfg {String} validateOnBlur @hide
23667      */
23668 });
23669
23670 Roo.HtmlEditorCore.white = [
23671         'area', 'br', 'img', 'input', 'hr', 'wbr',
23672         
23673        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23674        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23675        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23676        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23677        'table',   'ul',         'xmp', 
23678        
23679        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23680       'thead',   'tr', 
23681      
23682       'dir', 'menu', 'ol', 'ul', 'dl',
23683        
23684       'embed',  'object'
23685 ];
23686
23687
23688 Roo.HtmlEditorCore.black = [
23689     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23690         'applet', // 
23691         'base',   'basefont', 'bgsound', 'blink',  'body', 
23692         'frame',  'frameset', 'head',    'html',   'ilayer', 
23693         'iframe', 'layer',  'link',     'meta',    'object',   
23694         'script', 'style' ,'title',  'xml' // clean later..
23695 ];
23696 Roo.HtmlEditorCore.clean = [
23697     'script', 'style', 'title', 'xml'
23698 ];
23699 Roo.HtmlEditorCore.remove = [
23700     'font'
23701 ];
23702 // attributes..
23703
23704 Roo.HtmlEditorCore.ablack = [
23705     'on'
23706 ];
23707     
23708 Roo.HtmlEditorCore.aclean = [ 
23709     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23710 ];
23711
23712 // protocols..
23713 Roo.HtmlEditorCore.pwhite= [
23714         'http',  'https',  'mailto'
23715 ];
23716
23717 // white listed style attributes.
23718 Roo.HtmlEditorCore.cwhite= [
23719       //  'text-align', /// default is to allow most things..
23720       
23721          
23722 //        'font-size'//??
23723 ];
23724
23725 // black listed style attributes.
23726 Roo.HtmlEditorCore.cblack= [
23727       //  'font-size' -- this can be set by the project 
23728 ];
23729
23730
23731 Roo.HtmlEditorCore.swapCodes   =[ 
23732     [    8211, "--" ], 
23733     [    8212, "--" ], 
23734     [    8216,  "'" ],  
23735     [    8217, "'" ],  
23736     [    8220, '"' ],  
23737     [    8221, '"' ],  
23738     [    8226, "*" ],  
23739     [    8230, "..." ]
23740 ]; 
23741
23742     /*
23743  * - LGPL
23744  *
23745  * HtmlEditor
23746  * 
23747  */
23748
23749 /**
23750  * @class Roo.bootstrap.HtmlEditor
23751  * @extends Roo.bootstrap.TextArea
23752  * Bootstrap HtmlEditor class
23753
23754  * @constructor
23755  * Create a new HtmlEditor
23756  * @param {Object} config The config object
23757  */
23758
23759 Roo.bootstrap.HtmlEditor = function(config){
23760     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23761     if (!this.toolbars) {
23762         this.toolbars = [];
23763     }
23764     
23765     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23766     this.addEvents({
23767             /**
23768              * @event initialize
23769              * Fires when the editor is fully initialized (including the iframe)
23770              * @param {HtmlEditor} this
23771              */
23772             initialize: true,
23773             /**
23774              * @event activate
23775              * Fires when the editor is first receives the focus. Any insertion must wait
23776              * until after this event.
23777              * @param {HtmlEditor} this
23778              */
23779             activate: true,
23780              /**
23781              * @event beforesync
23782              * Fires before the textarea is updated with content from the editor iframe. Return false
23783              * to cancel the sync.
23784              * @param {HtmlEditor} this
23785              * @param {String} html
23786              */
23787             beforesync: true,
23788              /**
23789              * @event beforepush
23790              * Fires before the iframe editor is updated with content from the textarea. Return false
23791              * to cancel the push.
23792              * @param {HtmlEditor} this
23793              * @param {String} html
23794              */
23795             beforepush: true,
23796              /**
23797              * @event sync
23798              * Fires when the textarea is updated with content from the editor iframe.
23799              * @param {HtmlEditor} this
23800              * @param {String} html
23801              */
23802             sync: true,
23803              /**
23804              * @event push
23805              * Fires when the iframe editor is updated with content from the textarea.
23806              * @param {HtmlEditor} this
23807              * @param {String} html
23808              */
23809             push: true,
23810              /**
23811              * @event editmodechange
23812              * Fires when the editor switches edit modes
23813              * @param {HtmlEditor} this
23814              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23815              */
23816             editmodechange: true,
23817             /**
23818              * @event editorevent
23819              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23820              * @param {HtmlEditor} this
23821              */
23822             editorevent: true,
23823             /**
23824              * @event firstfocus
23825              * Fires when on first focus - needed by toolbars..
23826              * @param {HtmlEditor} this
23827              */
23828             firstfocus: true,
23829             /**
23830              * @event autosave
23831              * Auto save the htmlEditor value as a file into Events
23832              * @param {HtmlEditor} this
23833              */
23834             autosave: true,
23835             /**
23836              * @event savedpreview
23837              * preview the saved version of htmlEditor
23838              * @param {HtmlEditor} this
23839              */
23840             savedpreview: true
23841         });
23842 };
23843
23844
23845 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23846     
23847     
23848       /**
23849      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23850      */
23851     toolbars : false,
23852     
23853      /**
23854     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23855     */
23856     btns : [],
23857    
23858      /**
23859      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23860      *                        Roo.resizable.
23861      */
23862     resizable : false,
23863      /**
23864      * @cfg {Number} height (in pixels)
23865      */   
23866     height: 300,
23867    /**
23868      * @cfg {Number} width (in pixels)
23869      */   
23870     width: false,
23871     
23872     /**
23873      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23874      * 
23875      */
23876     stylesheets: false,
23877     
23878     // id of frame..
23879     frameId: false,
23880     
23881     // private properties
23882     validationEvent : false,
23883     deferHeight: true,
23884     initialized : false,
23885     activated : false,
23886     
23887     onFocus : Roo.emptyFn,
23888     iframePad:3,
23889     hideMode:'offsets',
23890     
23891     tbContainer : false,
23892     
23893     bodyCls : '',
23894     
23895     toolbarContainer :function() {
23896         return this.wrap.select('.x-html-editor-tb',true).first();
23897     },
23898
23899     /**
23900      * Protected method that will not generally be called directly. It
23901      * is called when the editor creates its toolbar. Override this method if you need to
23902      * add custom toolbar buttons.
23903      * @param {HtmlEditor} editor
23904      */
23905     createToolbar : function(){
23906         Roo.log('renewing');
23907         Roo.log("create toolbars");
23908         
23909         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23910         this.toolbars[0].render(this.toolbarContainer());
23911         
23912         return;
23913         
23914 //        if (!editor.toolbars || !editor.toolbars.length) {
23915 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23916 //        }
23917 //        
23918 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23919 //            editor.toolbars[i] = Roo.factory(
23920 //                    typeof(editor.toolbars[i]) == 'string' ?
23921 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23922 //                Roo.bootstrap.HtmlEditor);
23923 //            editor.toolbars[i].init(editor);
23924 //        }
23925     },
23926
23927      
23928     // private
23929     onRender : function(ct, position)
23930     {
23931        // Roo.log("Call onRender: " + this.xtype);
23932         var _t = this;
23933         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23934       
23935         this.wrap = this.inputEl().wrap({
23936             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23937         });
23938         
23939         this.editorcore.onRender(ct, position);
23940          
23941         if (this.resizable) {
23942             this.resizeEl = new Roo.Resizable(this.wrap, {
23943                 pinned : true,
23944                 wrap: true,
23945                 dynamic : true,
23946                 minHeight : this.height,
23947                 height: this.height,
23948                 handles : this.resizable,
23949                 width: this.width,
23950                 listeners : {
23951                     resize : function(r, w, h) {
23952                         _t.onResize(w,h); // -something
23953                     }
23954                 }
23955             });
23956             
23957         }
23958         this.createToolbar(this);
23959        
23960         
23961         if(!this.width && this.resizable){
23962             this.setSize(this.wrap.getSize());
23963         }
23964         if (this.resizeEl) {
23965             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23966             // should trigger onReize..
23967         }
23968         
23969     },
23970
23971     // private
23972     onResize : function(w, h)
23973     {
23974         Roo.log('resize: ' +w + ',' + h );
23975         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23976         var ew = false;
23977         var eh = false;
23978         
23979         if(this.inputEl() ){
23980             if(typeof w == 'number'){
23981                 var aw = w - this.wrap.getFrameWidth('lr');
23982                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23983                 ew = aw;
23984             }
23985             if(typeof h == 'number'){
23986                  var tbh = -11;  // fixme it needs to tool bar size!
23987                 for (var i =0; i < this.toolbars.length;i++) {
23988                     // fixme - ask toolbars for heights?
23989                     tbh += this.toolbars[i].el.getHeight();
23990                     //if (this.toolbars[i].footer) {
23991                     //    tbh += this.toolbars[i].footer.el.getHeight();
23992                     //}
23993                 }
23994               
23995                 
23996                 
23997                 
23998                 
23999                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24000                 ah -= 5; // knock a few pixes off for look..
24001                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
24002                 var eh = ah;
24003             }
24004         }
24005         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
24006         this.editorcore.onResize(ew,eh);
24007         
24008     },
24009
24010     /**
24011      * Toggles the editor between standard and source edit mode.
24012      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24013      */
24014     toggleSourceEdit : function(sourceEditMode)
24015     {
24016         this.editorcore.toggleSourceEdit(sourceEditMode);
24017         
24018         if(this.editorcore.sourceEditMode){
24019             Roo.log('editor - showing textarea');
24020             
24021 //            Roo.log('in');
24022 //            Roo.log(this.syncValue());
24023             this.syncValue();
24024             this.inputEl().removeClass(['hide', 'x-hidden']);
24025             this.inputEl().dom.removeAttribute('tabIndex');
24026             this.inputEl().focus();
24027         }else{
24028             Roo.log('editor - hiding textarea');
24029 //            Roo.log('out')
24030 //            Roo.log(this.pushValue()); 
24031             this.pushValue();
24032             
24033             this.inputEl().addClass(['hide', 'x-hidden']);
24034             this.inputEl().dom.setAttribute('tabIndex', -1);
24035             //this.deferFocus();
24036         }
24037          
24038         if(this.resizable){
24039             this.setSize(this.wrap.getSize());
24040         }
24041         
24042         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
24043     },
24044  
24045     // private (for BoxComponent)
24046     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24047
24048     // private (for BoxComponent)
24049     getResizeEl : function(){
24050         return this.wrap;
24051     },
24052
24053     // private (for BoxComponent)
24054     getPositionEl : function(){
24055         return this.wrap;
24056     },
24057
24058     // private
24059     initEvents : function(){
24060         this.originalValue = this.getValue();
24061     },
24062
24063 //    /**
24064 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24065 //     * @method
24066 //     */
24067 //    markInvalid : Roo.emptyFn,
24068 //    /**
24069 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24070 //     * @method
24071 //     */
24072 //    clearInvalid : Roo.emptyFn,
24073
24074     setValue : function(v){
24075         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
24076         this.editorcore.pushValue();
24077     },
24078
24079      
24080     // private
24081     deferFocus : function(){
24082         this.focus.defer(10, this);
24083     },
24084
24085     // doc'ed in Field
24086     focus : function(){
24087         this.editorcore.focus();
24088         
24089     },
24090       
24091
24092     // private
24093     onDestroy : function(){
24094         
24095         
24096         
24097         if(this.rendered){
24098             
24099             for (var i =0; i < this.toolbars.length;i++) {
24100                 // fixme - ask toolbars for heights?
24101                 this.toolbars[i].onDestroy();
24102             }
24103             
24104             this.wrap.dom.innerHTML = '';
24105             this.wrap.remove();
24106         }
24107     },
24108
24109     // private
24110     onFirstFocus : function(){
24111         //Roo.log("onFirstFocus");
24112         this.editorcore.onFirstFocus();
24113          for (var i =0; i < this.toolbars.length;i++) {
24114             this.toolbars[i].onFirstFocus();
24115         }
24116         
24117     },
24118     
24119     // private
24120     syncValue : function()
24121     {   
24122         this.editorcore.syncValue();
24123     },
24124     
24125     pushValue : function()
24126     {   
24127         this.editorcore.pushValue();
24128     }
24129      
24130     
24131     // hide stuff that is not compatible
24132     /**
24133      * @event blur
24134      * @hide
24135      */
24136     /**
24137      * @event change
24138      * @hide
24139      */
24140     /**
24141      * @event focus
24142      * @hide
24143      */
24144     /**
24145      * @event specialkey
24146      * @hide
24147      */
24148     /**
24149      * @cfg {String} fieldClass @hide
24150      */
24151     /**
24152      * @cfg {String} focusClass @hide
24153      */
24154     /**
24155      * @cfg {String} autoCreate @hide
24156      */
24157     /**
24158      * @cfg {String} inputType @hide
24159      */
24160      
24161     /**
24162      * @cfg {String} invalidText @hide
24163      */
24164     /**
24165      * @cfg {String} msgFx @hide
24166      */
24167     /**
24168      * @cfg {String} validateOnBlur @hide
24169      */
24170 });
24171  
24172     
24173    
24174    
24175    
24176       
24177 Roo.namespace('Roo.bootstrap.htmleditor');
24178 /**
24179  * @class Roo.bootstrap.HtmlEditorToolbar1
24180  * Basic Toolbar
24181  * 
24182  * @example
24183  * Usage:
24184  *
24185  new Roo.bootstrap.HtmlEditor({
24186     ....
24187     toolbars : [
24188         new Roo.bootstrap.HtmlEditorToolbar1({
24189             disable : { fonts: 1 , format: 1, ..., ... , ...],
24190             btns : [ .... ]
24191         })
24192     }
24193      
24194  * 
24195  * @cfg {Object} disable List of elements to disable..
24196  * @cfg {Array} btns List of additional buttons.
24197  * 
24198  * 
24199  * NEEDS Extra CSS? 
24200  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24201  */
24202  
24203 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24204 {
24205     
24206     Roo.apply(this, config);
24207     
24208     // default disabled, based on 'good practice'..
24209     this.disable = this.disable || {};
24210     Roo.applyIf(this.disable, {
24211         fontSize : true,
24212         colors : true,
24213         specialElements : true
24214     });
24215     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24216     
24217     this.editor = config.editor;
24218     this.editorcore = config.editor.editorcore;
24219     
24220     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24221     
24222     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24223     // dont call parent... till later.
24224 }
24225 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24226      
24227     bar : true,
24228     
24229     editor : false,
24230     editorcore : false,
24231     
24232     
24233     formats : [
24234         "p" ,  
24235         "h1","h2","h3","h4","h5","h6", 
24236         "pre", "code", 
24237         "abbr", "acronym", "address", "cite", "samp", "var",
24238         'div','span'
24239     ],
24240     
24241     onRender : function(ct, position)
24242     {
24243        // Roo.log("Call onRender: " + this.xtype);
24244         
24245        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24246        Roo.log(this.el);
24247        this.el.dom.style.marginBottom = '0';
24248        var _this = this;
24249        var editorcore = this.editorcore;
24250        var editor= this.editor;
24251        
24252        var children = [];
24253        var btn = function(id,cmd , toggle, handler, html){
24254        
24255             var  event = toggle ? 'toggle' : 'click';
24256        
24257             var a = {
24258                 size : 'sm',
24259                 xtype: 'Button',
24260                 xns: Roo.bootstrap,
24261                 //glyphicon : id,
24262                 fa: id,
24263                 cmd : id || cmd,
24264                 enableToggle:toggle !== false,
24265                 html : html || '',
24266                 pressed : toggle ? false : null,
24267                 listeners : {}
24268             };
24269             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24270                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24271             };
24272             children.push(a);
24273             return a;
24274        }
24275        
24276     //    var cb_box = function...
24277         
24278         var style = {
24279                 xtype: 'Button',
24280                 size : 'sm',
24281                 xns: Roo.bootstrap,
24282                 fa : 'font',
24283                 //html : 'submit'
24284                 menu : {
24285                     xtype: 'Menu',
24286                     xns: Roo.bootstrap,
24287                     items:  []
24288                 }
24289         };
24290         Roo.each(this.formats, function(f) {
24291             style.menu.items.push({
24292                 xtype :'MenuItem',
24293                 xns: Roo.bootstrap,
24294                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24295                 tagname : f,
24296                 listeners : {
24297                     click : function()
24298                     {
24299                         editorcore.insertTag(this.tagname);
24300                         editor.focus();
24301                     }
24302                 }
24303                 
24304             });
24305         });
24306         children.push(style);   
24307         
24308         btn('bold',false,true);
24309         btn('italic',false,true);
24310         btn('align-left', 'justifyleft',true);
24311         btn('align-center', 'justifycenter',true);
24312         btn('align-right' , 'justifyright',true);
24313         btn('link', false, false, function(btn) {
24314             //Roo.log("create link?");
24315             var url = prompt(this.createLinkText, this.defaultLinkValue);
24316             if(url && url != 'http:/'+'/'){
24317                 this.editorcore.relayCmd('createlink', url);
24318             }
24319         }),
24320         btn('list','insertunorderedlist',true);
24321         btn('pencil', false,true, function(btn){
24322                 Roo.log(this);
24323                 this.toggleSourceEdit(btn.pressed);
24324         });
24325         
24326         if (this.editor.btns.length > 0) {
24327             for (var i = 0; i<this.editor.btns.length; i++) {
24328                 children.push(this.editor.btns[i]);
24329             }
24330         }
24331         
24332         /*
24333         var cog = {
24334                 xtype: 'Button',
24335                 size : 'sm',
24336                 xns: Roo.bootstrap,
24337                 glyphicon : 'cog',
24338                 //html : 'submit'
24339                 menu : {
24340                     xtype: 'Menu',
24341                     xns: Roo.bootstrap,
24342                     items:  []
24343                 }
24344         };
24345         
24346         cog.menu.items.push({
24347             xtype :'MenuItem',
24348             xns: Roo.bootstrap,
24349             html : Clean styles,
24350             tagname : f,
24351             listeners : {
24352                 click : function()
24353                 {
24354                     editorcore.insertTag(this.tagname);
24355                     editor.focus();
24356                 }
24357             }
24358             
24359         });
24360        */
24361         
24362          
24363        this.xtype = 'NavSimplebar';
24364         
24365         for(var i=0;i< children.length;i++) {
24366             
24367             this.buttons.add(this.addxtypeChild(children[i]));
24368             
24369         }
24370         
24371         editor.on('editorevent', this.updateToolbar, this);
24372     },
24373     onBtnClick : function(id)
24374     {
24375        this.editorcore.relayCmd(id);
24376        this.editorcore.focus();
24377     },
24378     
24379     /**
24380      * Protected method that will not generally be called directly. It triggers
24381      * a toolbar update by reading the markup state of the current selection in the editor.
24382      */
24383     updateToolbar: function(){
24384
24385         if(!this.editorcore.activated){
24386             this.editor.onFirstFocus(); // is this neeed?
24387             return;
24388         }
24389
24390         var btns = this.buttons; 
24391         var doc = this.editorcore.doc;
24392         btns.get('bold').setActive(doc.queryCommandState('bold'));
24393         btns.get('italic').setActive(doc.queryCommandState('italic'));
24394         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24395         
24396         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24397         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24398         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24399         
24400         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24401         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24402          /*
24403         
24404         var ans = this.editorcore.getAllAncestors();
24405         if (this.formatCombo) {
24406             
24407             
24408             var store = this.formatCombo.store;
24409             this.formatCombo.setValue("");
24410             for (var i =0; i < ans.length;i++) {
24411                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24412                     // select it..
24413                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24414                     break;
24415                 }
24416             }
24417         }
24418         
24419         
24420         
24421         // hides menus... - so this cant be on a menu...
24422         Roo.bootstrap.MenuMgr.hideAll();
24423         */
24424         Roo.bootstrap.MenuMgr.hideAll();
24425         //this.editorsyncValue();
24426     },
24427     onFirstFocus: function() {
24428         this.buttons.each(function(item){
24429            item.enable();
24430         });
24431     },
24432     toggleSourceEdit : function(sourceEditMode){
24433         
24434           
24435         if(sourceEditMode){
24436             Roo.log("disabling buttons");
24437            this.buttons.each( function(item){
24438                 if(item.cmd != 'pencil'){
24439                     item.disable();
24440                 }
24441             });
24442           
24443         }else{
24444             Roo.log("enabling buttons");
24445             if(this.editorcore.initialized){
24446                 this.buttons.each( function(item){
24447                     item.enable();
24448                 });
24449             }
24450             
24451         }
24452         Roo.log("calling toggole on editor");
24453         // tell the editor that it's been pressed..
24454         this.editor.toggleSourceEdit(sourceEditMode);
24455        
24456     }
24457 });
24458
24459
24460
24461
24462
24463 /**
24464  * @class Roo.bootstrap.Table.AbstractSelectionModel
24465  * @extends Roo.util.Observable
24466  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24467  * implemented by descendant classes.  This class should not be directly instantiated.
24468  * @constructor
24469  */
24470 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24471     this.locked = false;
24472     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24473 };
24474
24475
24476 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24477     /** @ignore Called by the grid automatically. Do not call directly. */
24478     init : function(grid){
24479         this.grid = grid;
24480         this.initEvents();
24481     },
24482
24483     /**
24484      * Locks the selections.
24485      */
24486     lock : function(){
24487         this.locked = true;
24488     },
24489
24490     /**
24491      * Unlocks the selections.
24492      */
24493     unlock : function(){
24494         this.locked = false;
24495     },
24496
24497     /**
24498      * Returns true if the selections are locked.
24499      * @return {Boolean}
24500      */
24501     isLocked : function(){
24502         return this.locked;
24503     }
24504 });
24505 /**
24506  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24507  * @class Roo.bootstrap.Table.RowSelectionModel
24508  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24509  * It supports multiple selections and keyboard selection/navigation. 
24510  * @constructor
24511  * @param {Object} config
24512  */
24513
24514 Roo.bootstrap.Table.RowSelectionModel = function(config){
24515     Roo.apply(this, config);
24516     this.selections = new Roo.util.MixedCollection(false, function(o){
24517         return o.id;
24518     });
24519
24520     this.last = false;
24521     this.lastActive = false;
24522
24523     this.addEvents({
24524         /**
24525              * @event selectionchange
24526              * Fires when the selection changes
24527              * @param {SelectionModel} this
24528              */
24529             "selectionchange" : true,
24530         /**
24531              * @event afterselectionchange
24532              * Fires after the selection changes (eg. by key press or clicking)
24533              * @param {SelectionModel} this
24534              */
24535             "afterselectionchange" : true,
24536         /**
24537              * @event beforerowselect
24538              * Fires when a row is selected being selected, return false to cancel.
24539              * @param {SelectionModel} this
24540              * @param {Number} rowIndex The selected index
24541              * @param {Boolean} keepExisting False if other selections will be cleared
24542              */
24543             "beforerowselect" : true,
24544         /**
24545              * @event rowselect
24546              * Fires when a row is selected.
24547              * @param {SelectionModel} this
24548              * @param {Number} rowIndex The selected index
24549              * @param {Roo.data.Record} r The record
24550              */
24551             "rowselect" : true,
24552         /**
24553              * @event rowdeselect
24554              * Fires when a row is deselected.
24555              * @param {SelectionModel} this
24556              * @param {Number} rowIndex The selected index
24557              */
24558         "rowdeselect" : true
24559     });
24560     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24561     this.locked = false;
24562  };
24563
24564 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24565     /**
24566      * @cfg {Boolean} singleSelect
24567      * True to allow selection of only one row at a time (defaults to false)
24568      */
24569     singleSelect : false,
24570
24571     // private
24572     initEvents : function()
24573     {
24574
24575         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24576         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24577         //}else{ // allow click to work like normal
24578          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24579         //}
24580         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24581         this.grid.on("rowclick", this.handleMouseDown, this);
24582         
24583         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24584             "up" : function(e){
24585                 if(!e.shiftKey){
24586                     this.selectPrevious(e.shiftKey);
24587                 }else if(this.last !== false && this.lastActive !== false){
24588                     var last = this.last;
24589                     this.selectRange(this.last,  this.lastActive-1);
24590                     this.grid.getView().focusRow(this.lastActive);
24591                     if(last !== false){
24592                         this.last = last;
24593                     }
24594                 }else{
24595                     this.selectFirstRow();
24596                 }
24597                 this.fireEvent("afterselectionchange", this);
24598             },
24599             "down" : function(e){
24600                 if(!e.shiftKey){
24601                     this.selectNext(e.shiftKey);
24602                 }else if(this.last !== false && this.lastActive !== false){
24603                     var last = this.last;
24604                     this.selectRange(this.last,  this.lastActive+1);
24605                     this.grid.getView().focusRow(this.lastActive);
24606                     if(last !== false){
24607                         this.last = last;
24608                     }
24609                 }else{
24610                     this.selectFirstRow();
24611                 }
24612                 this.fireEvent("afterselectionchange", this);
24613             },
24614             scope: this
24615         });
24616         this.grid.store.on('load', function(){
24617             this.selections.clear();
24618         },this);
24619         /*
24620         var view = this.grid.view;
24621         view.on("refresh", this.onRefresh, this);
24622         view.on("rowupdated", this.onRowUpdated, this);
24623         view.on("rowremoved", this.onRemove, this);
24624         */
24625     },
24626
24627     // private
24628     onRefresh : function()
24629     {
24630         var ds = this.grid.store, i, v = this.grid.view;
24631         var s = this.selections;
24632         s.each(function(r){
24633             if((i = ds.indexOfId(r.id)) != -1){
24634                 v.onRowSelect(i);
24635             }else{
24636                 s.remove(r);
24637             }
24638         });
24639     },
24640
24641     // private
24642     onRemove : function(v, index, r){
24643         this.selections.remove(r);
24644     },
24645
24646     // private
24647     onRowUpdated : function(v, index, r){
24648         if(this.isSelected(r)){
24649             v.onRowSelect(index);
24650         }
24651     },
24652
24653     /**
24654      * Select records.
24655      * @param {Array} records The records to select
24656      * @param {Boolean} keepExisting (optional) True to keep existing selections
24657      */
24658     selectRecords : function(records, keepExisting)
24659     {
24660         if(!keepExisting){
24661             this.clearSelections();
24662         }
24663             var ds = this.grid.store;
24664         for(var i = 0, len = records.length; i < len; i++){
24665             this.selectRow(ds.indexOf(records[i]), true);
24666         }
24667     },
24668
24669     /**
24670      * Gets the number of selected rows.
24671      * @return {Number}
24672      */
24673     getCount : function(){
24674         return this.selections.length;
24675     },
24676
24677     /**
24678      * Selects the first row in the grid.
24679      */
24680     selectFirstRow : function(){
24681         this.selectRow(0);
24682     },
24683
24684     /**
24685      * Select the last row.
24686      * @param {Boolean} keepExisting (optional) True to keep existing selections
24687      */
24688     selectLastRow : function(keepExisting){
24689         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24690         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24691     },
24692
24693     /**
24694      * Selects the row immediately following the last selected row.
24695      * @param {Boolean} keepExisting (optional) True to keep existing selections
24696      */
24697     selectNext : function(keepExisting)
24698     {
24699             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24700             this.selectRow(this.last+1, keepExisting);
24701             this.grid.getView().focusRow(this.last);
24702         }
24703     },
24704
24705     /**
24706      * Selects the row that precedes the last selected row.
24707      * @param {Boolean} keepExisting (optional) True to keep existing selections
24708      */
24709     selectPrevious : function(keepExisting){
24710         if(this.last){
24711             this.selectRow(this.last-1, keepExisting);
24712             this.grid.getView().focusRow(this.last);
24713         }
24714     },
24715
24716     /**
24717      * Returns the selected records
24718      * @return {Array} Array of selected records
24719      */
24720     getSelections : function(){
24721         return [].concat(this.selections.items);
24722     },
24723
24724     /**
24725      * Returns the first selected record.
24726      * @return {Record}
24727      */
24728     getSelected : function(){
24729         return this.selections.itemAt(0);
24730     },
24731
24732
24733     /**
24734      * Clears all selections.
24735      */
24736     clearSelections : function(fast)
24737     {
24738         if(this.locked) {
24739             return;
24740         }
24741         if(fast !== true){
24742                 var ds = this.grid.store;
24743             var s = this.selections;
24744             s.each(function(r){
24745                 this.deselectRow(ds.indexOfId(r.id));
24746             }, this);
24747             s.clear();
24748         }else{
24749             this.selections.clear();
24750         }
24751         this.last = false;
24752     },
24753
24754
24755     /**
24756      * Selects all rows.
24757      */
24758     selectAll : function(){
24759         if(this.locked) {
24760             return;
24761         }
24762         this.selections.clear();
24763         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24764             this.selectRow(i, true);
24765         }
24766     },
24767
24768     /**
24769      * Returns True if there is a selection.
24770      * @return {Boolean}
24771      */
24772     hasSelection : function(){
24773         return this.selections.length > 0;
24774     },
24775
24776     /**
24777      * Returns True if the specified row is selected.
24778      * @param {Number/Record} record The record or index of the record to check
24779      * @return {Boolean}
24780      */
24781     isSelected : function(index){
24782             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24783         return (r && this.selections.key(r.id) ? true : false);
24784     },
24785
24786     /**
24787      * Returns True if the specified record id is selected.
24788      * @param {String} id The id of record to check
24789      * @return {Boolean}
24790      */
24791     isIdSelected : function(id){
24792         return (this.selections.key(id) ? true : false);
24793     },
24794
24795
24796     // private
24797     handleMouseDBClick : function(e, t){
24798         
24799     },
24800     // private
24801     handleMouseDown : function(e, t)
24802     {
24803             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24804         if(this.isLocked() || rowIndex < 0 ){
24805             return;
24806         };
24807         if(e.shiftKey && this.last !== false){
24808             var last = this.last;
24809             this.selectRange(last, rowIndex, e.ctrlKey);
24810             this.last = last; // reset the last
24811             t.focus();
24812     
24813         }else{
24814             var isSelected = this.isSelected(rowIndex);
24815             //Roo.log("select row:" + rowIndex);
24816             if(isSelected){
24817                 this.deselectRow(rowIndex);
24818             } else {
24819                         this.selectRow(rowIndex, true);
24820             }
24821     
24822             /*
24823                 if(e.button !== 0 && isSelected){
24824                 alert('rowIndex 2: ' + rowIndex);
24825                     view.focusRow(rowIndex);
24826                 }else if(e.ctrlKey && isSelected){
24827                     this.deselectRow(rowIndex);
24828                 }else if(!isSelected){
24829                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24830                     view.focusRow(rowIndex);
24831                 }
24832             */
24833         }
24834         this.fireEvent("afterselectionchange", this);
24835     },
24836     // private
24837     handleDragableRowClick :  function(grid, rowIndex, e) 
24838     {
24839         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24840             this.selectRow(rowIndex, false);
24841             grid.view.focusRow(rowIndex);
24842              this.fireEvent("afterselectionchange", this);
24843         }
24844     },
24845     
24846     /**
24847      * Selects multiple rows.
24848      * @param {Array} rows Array of the indexes of the row to select
24849      * @param {Boolean} keepExisting (optional) True to keep existing selections
24850      */
24851     selectRows : function(rows, keepExisting){
24852         if(!keepExisting){
24853             this.clearSelections();
24854         }
24855         for(var i = 0, len = rows.length; i < len; i++){
24856             this.selectRow(rows[i], true);
24857         }
24858     },
24859
24860     /**
24861      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24862      * @param {Number} startRow The index of the first row in the range
24863      * @param {Number} endRow The index of the last row in the range
24864      * @param {Boolean} keepExisting (optional) True to retain existing selections
24865      */
24866     selectRange : function(startRow, endRow, keepExisting){
24867         if(this.locked) {
24868             return;
24869         }
24870         if(!keepExisting){
24871             this.clearSelections();
24872         }
24873         if(startRow <= endRow){
24874             for(var i = startRow; i <= endRow; i++){
24875                 this.selectRow(i, true);
24876             }
24877         }else{
24878             for(var i = startRow; i >= endRow; i--){
24879                 this.selectRow(i, true);
24880             }
24881         }
24882     },
24883
24884     /**
24885      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24886      * @param {Number} startRow The index of the first row in the range
24887      * @param {Number} endRow The index of the last row in the range
24888      */
24889     deselectRange : function(startRow, endRow, preventViewNotify){
24890         if(this.locked) {
24891             return;
24892         }
24893         for(var i = startRow; i <= endRow; i++){
24894             this.deselectRow(i, preventViewNotify);
24895         }
24896     },
24897
24898     /**
24899      * Selects a row.
24900      * @param {Number} row The index of the row to select
24901      * @param {Boolean} keepExisting (optional) True to keep existing selections
24902      */
24903     selectRow : function(index, keepExisting, preventViewNotify)
24904     {
24905             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24906             return;
24907         }
24908         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24909             if(!keepExisting || this.singleSelect){
24910                 this.clearSelections();
24911             }
24912             
24913             var r = this.grid.store.getAt(index);
24914             //console.log('selectRow - record id :' + r.id);
24915             
24916             this.selections.add(r);
24917             this.last = this.lastActive = index;
24918             if(!preventViewNotify){
24919                 var proxy = new Roo.Element(
24920                                 this.grid.getRowDom(index)
24921                 );
24922                 proxy.addClass('bg-info info');
24923             }
24924             this.fireEvent("rowselect", this, index, r);
24925             this.fireEvent("selectionchange", this);
24926         }
24927     },
24928
24929     /**
24930      * Deselects a row.
24931      * @param {Number} row The index of the row to deselect
24932      */
24933     deselectRow : function(index, preventViewNotify)
24934     {
24935         if(this.locked) {
24936             return;
24937         }
24938         if(this.last == index){
24939             this.last = false;
24940         }
24941         if(this.lastActive == index){
24942             this.lastActive = false;
24943         }
24944         
24945         var r = this.grid.store.getAt(index);
24946         if (!r) {
24947             return;
24948         }
24949         
24950         this.selections.remove(r);
24951         //.console.log('deselectRow - record id :' + r.id);
24952         if(!preventViewNotify){
24953         
24954             var proxy = new Roo.Element(
24955                 this.grid.getRowDom(index)
24956             );
24957             proxy.removeClass('bg-info info');
24958         }
24959         this.fireEvent("rowdeselect", this, index);
24960         this.fireEvent("selectionchange", this);
24961     },
24962
24963     // private
24964     restoreLast : function(){
24965         if(this._last){
24966             this.last = this._last;
24967         }
24968     },
24969
24970     // private
24971     acceptsNav : function(row, col, cm){
24972         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24973     },
24974
24975     // private
24976     onEditorKey : function(field, e){
24977         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24978         if(k == e.TAB){
24979             e.stopEvent();
24980             ed.completeEdit();
24981             if(e.shiftKey){
24982                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24983             }else{
24984                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24985             }
24986         }else if(k == e.ENTER && !e.ctrlKey){
24987             e.stopEvent();
24988             ed.completeEdit();
24989             if(e.shiftKey){
24990                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24991             }else{
24992                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24993             }
24994         }else if(k == e.ESC){
24995             ed.cancelEdit();
24996         }
24997         if(newCell){
24998             g.startEditing(newCell[0], newCell[1]);
24999         }
25000     }
25001 });
25002 /*
25003  * Based on:
25004  * Ext JS Library 1.1.1
25005  * Copyright(c) 2006-2007, Ext JS, LLC.
25006  *
25007  * Originally Released Under LGPL - original licence link has changed is not relivant.
25008  *
25009  * Fork - LGPL
25010  * <script type="text/javascript">
25011  */
25012  
25013 /**
25014  * @class Roo.bootstrap.PagingToolbar
25015  * @extends Roo.bootstrap.NavSimplebar
25016  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
25017  * @constructor
25018  * Create a new PagingToolbar
25019  * @param {Object} config The config object
25020  * @param {Roo.data.Store} store
25021  */
25022 Roo.bootstrap.PagingToolbar = function(config)
25023 {
25024     // old args format still supported... - xtype is prefered..
25025         // created from xtype...
25026     
25027     this.ds = config.dataSource;
25028     
25029     if (config.store && !this.ds) {
25030         this.store= Roo.factory(config.store, Roo.data);
25031         this.ds = this.store;
25032         this.ds.xmodule = this.xmodule || false;
25033     }
25034     
25035     this.toolbarItems = [];
25036     if (config.items) {
25037         this.toolbarItems = config.items;
25038     }
25039     
25040     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
25041     
25042     this.cursor = 0;
25043     
25044     if (this.ds) { 
25045         this.bind(this.ds);
25046     }
25047     
25048     if (Roo.bootstrap.version == 4) {
25049         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
25050     } else {
25051         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
25052     }
25053     
25054 };
25055
25056 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
25057     /**
25058      * @cfg {Roo.data.Store} dataSource
25059      * The underlying data store providing the paged data
25060      */
25061     /**
25062      * @cfg {String/HTMLElement/Element} container
25063      * container The id or element that will contain the toolbar
25064      */
25065     /**
25066      * @cfg {Boolean} displayInfo
25067      * True to display the displayMsg (defaults to false)
25068      */
25069     /**
25070      * @cfg {Number} pageSize
25071      * The number of records to display per page (defaults to 20)
25072      */
25073     pageSize: 20,
25074     /**
25075      * @cfg {String} displayMsg
25076      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
25077      */
25078     displayMsg : 'Displaying {0} - {1} of {2}',
25079     /**
25080      * @cfg {String} emptyMsg
25081      * The message to display when no records are found (defaults to "No data to display")
25082      */
25083     emptyMsg : 'No data to display',
25084     /**
25085      * Customizable piece of the default paging text (defaults to "Page")
25086      * @type String
25087      */
25088     beforePageText : "Page",
25089     /**
25090      * Customizable piece of the default paging text (defaults to "of %0")
25091      * @type String
25092      */
25093     afterPageText : "of {0}",
25094     /**
25095      * Customizable piece of the default paging text (defaults to "First Page")
25096      * @type String
25097      */
25098     firstText : "First Page",
25099     /**
25100      * Customizable piece of the default paging text (defaults to "Previous Page")
25101      * @type String
25102      */
25103     prevText : "Previous Page",
25104     /**
25105      * Customizable piece of the default paging text (defaults to "Next Page")
25106      * @type String
25107      */
25108     nextText : "Next Page",
25109     /**
25110      * Customizable piece of the default paging text (defaults to "Last Page")
25111      * @type String
25112      */
25113     lastText : "Last Page",
25114     /**
25115      * Customizable piece of the default paging text (defaults to "Refresh")
25116      * @type String
25117      */
25118     refreshText : "Refresh",
25119
25120     buttons : false,
25121     // private
25122     onRender : function(ct, position) 
25123     {
25124         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25125         this.navgroup.parentId = this.id;
25126         this.navgroup.onRender(this.el, null);
25127         // add the buttons to the navgroup
25128         
25129         if(this.displayInfo){
25130             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25131             this.displayEl = this.el.select('.x-paging-info', true).first();
25132 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25133 //            this.displayEl = navel.el.select('span',true).first();
25134         }
25135         
25136         var _this = this;
25137         
25138         if(this.buttons){
25139             Roo.each(_this.buttons, function(e){ // this might need to use render????
25140                Roo.factory(e).render(_this.el);
25141             });
25142         }
25143             
25144         Roo.each(_this.toolbarItems, function(e) {
25145             _this.navgroup.addItem(e);
25146         });
25147         
25148         
25149         this.first = this.navgroup.addItem({
25150             tooltip: this.firstText,
25151             cls: "prev btn-outline-secondary",
25152             html : ' <i class="fa fa-step-backward"></i>',
25153             disabled: true,
25154             preventDefault: true,
25155             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25156         });
25157         
25158         this.prev =  this.navgroup.addItem({
25159             tooltip: this.prevText,
25160             cls: "prev btn-outline-secondary",
25161             html : ' <i class="fa fa-backward"></i>',
25162             disabled: true,
25163             preventDefault: true,
25164             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25165         });
25166     //this.addSeparator();
25167         
25168         
25169         var field = this.navgroup.addItem( {
25170             tagtype : 'span',
25171             cls : 'x-paging-position  btn-outline-secondary',
25172              disabled: true,
25173             html : this.beforePageText  +
25174                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25175                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25176          } ); //?? escaped?
25177         
25178         this.field = field.el.select('input', true).first();
25179         this.field.on("keydown", this.onPagingKeydown, this);
25180         this.field.on("focus", function(){this.dom.select();});
25181     
25182     
25183         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25184         //this.field.setHeight(18);
25185         //this.addSeparator();
25186         this.next = this.navgroup.addItem({
25187             tooltip: this.nextText,
25188             cls: "next btn-outline-secondary",
25189             html : ' <i class="fa fa-forward"></i>',
25190             disabled: true,
25191             preventDefault: true,
25192             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25193         });
25194         this.last = this.navgroup.addItem({
25195             tooltip: this.lastText,
25196             html : ' <i class="fa fa-step-forward"></i>',
25197             cls: "next btn-outline-secondary",
25198             disabled: true,
25199             preventDefault: true,
25200             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25201         });
25202     //this.addSeparator();
25203         this.loading = this.navgroup.addItem({
25204             tooltip: this.refreshText,
25205             cls: "btn-outline-secondary",
25206             html : ' <i class="fa fa-refresh"></i>',
25207             preventDefault: true,
25208             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25209         });
25210         
25211     },
25212
25213     // private
25214     updateInfo : function(){
25215         if(this.displayEl){
25216             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25217             var msg = count == 0 ?
25218                 this.emptyMsg :
25219                 String.format(
25220                     this.displayMsg,
25221                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25222                 );
25223             this.displayEl.update(msg);
25224         }
25225     },
25226
25227     // private
25228     onLoad : function(ds, r, o)
25229     {
25230         this.cursor = o.params.start ? o.params.start : 0;
25231         
25232         var d = this.getPageData(),
25233             ap = d.activePage,
25234             ps = d.pages;
25235         
25236         
25237         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25238         this.field.dom.value = ap;
25239         this.first.setDisabled(ap == 1);
25240         this.prev.setDisabled(ap == 1);
25241         this.next.setDisabled(ap == ps);
25242         this.last.setDisabled(ap == ps);
25243         this.loading.enable();
25244         this.updateInfo();
25245     },
25246
25247     // private
25248     getPageData : function(){
25249         var total = this.ds.getTotalCount();
25250         return {
25251             total : total,
25252             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25253             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25254         };
25255     },
25256
25257     // private
25258     onLoadError : function(){
25259         this.loading.enable();
25260     },
25261
25262     // private
25263     onPagingKeydown : function(e){
25264         var k = e.getKey();
25265         var d = this.getPageData();
25266         if(k == e.RETURN){
25267             var v = this.field.dom.value, pageNum;
25268             if(!v || isNaN(pageNum = parseInt(v, 10))){
25269                 this.field.dom.value = d.activePage;
25270                 return;
25271             }
25272             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25273             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25274             e.stopEvent();
25275         }
25276         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))
25277         {
25278           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25279           this.field.dom.value = pageNum;
25280           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25281           e.stopEvent();
25282         }
25283         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25284         {
25285           var v = this.field.dom.value, pageNum; 
25286           var increment = (e.shiftKey) ? 10 : 1;
25287           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25288                 increment *= -1;
25289           }
25290           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25291             this.field.dom.value = d.activePage;
25292             return;
25293           }
25294           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25295           {
25296             this.field.dom.value = parseInt(v, 10) + increment;
25297             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25298             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25299           }
25300           e.stopEvent();
25301         }
25302     },
25303
25304     // private
25305     beforeLoad : function(){
25306         if(this.loading){
25307             this.loading.disable();
25308         }
25309     },
25310
25311     // private
25312     onClick : function(which){
25313         
25314         var ds = this.ds;
25315         if (!ds) {
25316             return;
25317         }
25318         
25319         switch(which){
25320             case "first":
25321                 ds.load({params:{start: 0, limit: this.pageSize}});
25322             break;
25323             case "prev":
25324                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25325             break;
25326             case "next":
25327                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25328             break;
25329             case "last":
25330                 var total = ds.getTotalCount();
25331                 var extra = total % this.pageSize;
25332                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25333                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25334             break;
25335             case "refresh":
25336                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25337             break;
25338         }
25339     },
25340
25341     /**
25342      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25343      * @param {Roo.data.Store} store The data store to unbind
25344      */
25345     unbind : function(ds){
25346         ds.un("beforeload", this.beforeLoad, this);
25347         ds.un("load", this.onLoad, this);
25348         ds.un("loadexception", this.onLoadError, this);
25349         ds.un("remove", this.updateInfo, this);
25350         ds.un("add", this.updateInfo, this);
25351         this.ds = undefined;
25352     },
25353
25354     /**
25355      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25356      * @param {Roo.data.Store} store The data store to bind
25357      */
25358     bind : function(ds){
25359         ds.on("beforeload", this.beforeLoad, this);
25360         ds.on("load", this.onLoad, this);
25361         ds.on("loadexception", this.onLoadError, this);
25362         ds.on("remove", this.updateInfo, this);
25363         ds.on("add", this.updateInfo, this);
25364         this.ds = ds;
25365     }
25366 });/*
25367  * - LGPL
25368  *
25369  * element
25370  * 
25371  */
25372
25373 /**
25374  * @class Roo.bootstrap.MessageBar
25375  * @extends Roo.bootstrap.Component
25376  * Bootstrap MessageBar class
25377  * @cfg {String} html contents of the MessageBar
25378  * @cfg {String} weight (info | success | warning | danger) default info
25379  * @cfg {String} beforeClass insert the bar before the given class
25380  * @cfg {Boolean} closable (true | false) default false
25381  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25382  * 
25383  * @constructor
25384  * Create a new Element
25385  * @param {Object} config The config object
25386  */
25387
25388 Roo.bootstrap.MessageBar = function(config){
25389     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25390 };
25391
25392 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25393     
25394     html: '',
25395     weight: 'info',
25396     closable: false,
25397     fixed: false,
25398     beforeClass: 'bootstrap-sticky-wrap',
25399     
25400     getAutoCreate : function(){
25401         
25402         var cfg = {
25403             tag: 'div',
25404             cls: 'alert alert-dismissable alert-' + this.weight,
25405             cn: [
25406                 {
25407                     tag: 'span',
25408                     cls: 'message',
25409                     html: this.html || ''
25410                 }
25411             ]
25412         };
25413         
25414         if(this.fixed){
25415             cfg.cls += ' alert-messages-fixed';
25416         }
25417         
25418         if(this.closable){
25419             cfg.cn.push({
25420                 tag: 'button',
25421                 cls: 'close',
25422                 html: 'x'
25423             });
25424         }
25425         
25426         return cfg;
25427     },
25428     
25429     onRender : function(ct, position)
25430     {
25431         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25432         
25433         if(!this.el){
25434             var cfg = Roo.apply({},  this.getAutoCreate());
25435             cfg.id = Roo.id();
25436             
25437             if (this.cls) {
25438                 cfg.cls += ' ' + this.cls;
25439             }
25440             if (this.style) {
25441                 cfg.style = this.style;
25442             }
25443             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25444             
25445             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25446         }
25447         
25448         this.el.select('>button.close').on('click', this.hide, this);
25449         
25450     },
25451     
25452     show : function()
25453     {
25454         if (!this.rendered) {
25455             this.render();
25456         }
25457         
25458         this.el.show();
25459         
25460         this.fireEvent('show', this);
25461         
25462     },
25463     
25464     hide : function()
25465     {
25466         if (!this.rendered) {
25467             this.render();
25468         }
25469         
25470         this.el.hide();
25471         
25472         this.fireEvent('hide', this);
25473     },
25474     
25475     update : function()
25476     {
25477 //        var e = this.el.dom.firstChild;
25478 //        
25479 //        if(this.closable){
25480 //            e = e.nextSibling;
25481 //        }
25482 //        
25483 //        e.data = this.html || '';
25484
25485         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25486     }
25487    
25488 });
25489
25490  
25491
25492      /*
25493  * - LGPL
25494  *
25495  * Graph
25496  * 
25497  */
25498
25499
25500 /**
25501  * @class Roo.bootstrap.Graph
25502  * @extends Roo.bootstrap.Component
25503  * Bootstrap Graph class
25504 > Prameters
25505  -sm {number} sm 4
25506  -md {number} md 5
25507  @cfg {String} graphtype  bar | vbar | pie
25508  @cfg {number} g_x coodinator | centre x (pie)
25509  @cfg {number} g_y coodinator | centre y (pie)
25510  @cfg {number} g_r radius (pie)
25511  @cfg {number} g_height height of the chart (respected by all elements in the set)
25512  @cfg {number} g_width width of the chart (respected by all elements in the set)
25513  @cfg {Object} title The title of the chart
25514     
25515  -{Array}  values
25516  -opts (object) options for the chart 
25517      o {
25518      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25519      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25520      o vgutter (number)
25521      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.
25522      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25523      o to
25524      o stretch (boolean)
25525      o }
25526  -opts (object) options for the pie
25527      o{
25528      o cut
25529      o startAngle (number)
25530      o endAngle (number)
25531      } 
25532  *
25533  * @constructor
25534  * Create a new Input
25535  * @param {Object} config The config object
25536  */
25537
25538 Roo.bootstrap.Graph = function(config){
25539     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25540     
25541     this.addEvents({
25542         // img events
25543         /**
25544          * @event click
25545          * The img click event for the img.
25546          * @param {Roo.EventObject} e
25547          */
25548         "click" : true
25549     });
25550 };
25551
25552 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25553     
25554     sm: 4,
25555     md: 5,
25556     graphtype: 'bar',
25557     g_height: 250,
25558     g_width: 400,
25559     g_x: 50,
25560     g_y: 50,
25561     g_r: 30,
25562     opts:{
25563         //g_colors: this.colors,
25564         g_type: 'soft',
25565         g_gutter: '20%'
25566
25567     },
25568     title : false,
25569
25570     getAutoCreate : function(){
25571         
25572         var cfg = {
25573             tag: 'div',
25574             html : null
25575         };
25576         
25577         
25578         return  cfg;
25579     },
25580
25581     onRender : function(ct,position){
25582         
25583         
25584         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25585         
25586         if (typeof(Raphael) == 'undefined') {
25587             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25588             return;
25589         }
25590         
25591         this.raphael = Raphael(this.el.dom);
25592         
25593                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25594                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25595                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25596                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25597                 /*
25598                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25599                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25600                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25601                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25602                 
25603                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25604                 r.barchart(330, 10, 300, 220, data1);
25605                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25606                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25607                 */
25608                 
25609                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25610                 // r.barchart(30, 30, 560, 250,  xdata, {
25611                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25612                 //     axis : "0 0 1 1",
25613                 //     axisxlabels :  xdata
25614                 //     //yvalues : cols,
25615                    
25616                 // });
25617 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25618 //        
25619 //        this.load(null,xdata,{
25620 //                axis : "0 0 1 1",
25621 //                axisxlabels :  xdata
25622 //                });
25623
25624     },
25625
25626     load : function(graphtype,xdata,opts)
25627     {
25628         this.raphael.clear();
25629         if(!graphtype) {
25630             graphtype = this.graphtype;
25631         }
25632         if(!opts){
25633             opts = this.opts;
25634         }
25635         var r = this.raphael,
25636             fin = function () {
25637                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25638             },
25639             fout = function () {
25640                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25641             },
25642             pfin = function() {
25643                 this.sector.stop();
25644                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25645
25646                 if (this.label) {
25647                     this.label[0].stop();
25648                     this.label[0].attr({ r: 7.5 });
25649                     this.label[1].attr({ "font-weight": 800 });
25650                 }
25651             },
25652             pfout = function() {
25653                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25654
25655                 if (this.label) {
25656                     this.label[0].animate({ r: 5 }, 500, "bounce");
25657                     this.label[1].attr({ "font-weight": 400 });
25658                 }
25659             };
25660
25661         switch(graphtype){
25662             case 'bar':
25663                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25664                 break;
25665             case 'hbar':
25666                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25667                 break;
25668             case 'pie':
25669 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25670 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25671 //            
25672                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25673                 
25674                 break;
25675
25676         }
25677         
25678         if(this.title){
25679             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25680         }
25681         
25682     },
25683     
25684     setTitle: function(o)
25685     {
25686         this.title = o;
25687     },
25688     
25689     initEvents: function() {
25690         
25691         if(!this.href){
25692             this.el.on('click', this.onClick, this);
25693         }
25694     },
25695     
25696     onClick : function(e)
25697     {
25698         Roo.log('img onclick');
25699         this.fireEvent('click', this, e);
25700     }
25701    
25702 });
25703
25704  
25705 /*
25706  * - LGPL
25707  *
25708  * numberBox
25709  * 
25710  */
25711 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25712
25713 /**
25714  * @class Roo.bootstrap.dash.NumberBox
25715  * @extends Roo.bootstrap.Component
25716  * Bootstrap NumberBox class
25717  * @cfg {String} headline Box headline
25718  * @cfg {String} content Box content
25719  * @cfg {String} icon Box icon
25720  * @cfg {String} footer Footer text
25721  * @cfg {String} fhref Footer href
25722  * 
25723  * @constructor
25724  * Create a new NumberBox
25725  * @param {Object} config The config object
25726  */
25727
25728
25729 Roo.bootstrap.dash.NumberBox = function(config){
25730     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25731     
25732 };
25733
25734 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25735     
25736     headline : '',
25737     content : '',
25738     icon : '',
25739     footer : '',
25740     fhref : '',
25741     ficon : '',
25742     
25743     getAutoCreate : function(){
25744         
25745         var cfg = {
25746             tag : 'div',
25747             cls : 'small-box ',
25748             cn : [
25749                 {
25750                     tag : 'div',
25751                     cls : 'inner',
25752                     cn :[
25753                         {
25754                             tag : 'h3',
25755                             cls : 'roo-headline',
25756                             html : this.headline
25757                         },
25758                         {
25759                             tag : 'p',
25760                             cls : 'roo-content',
25761                             html : this.content
25762                         }
25763                     ]
25764                 }
25765             ]
25766         };
25767         
25768         if(this.icon){
25769             cfg.cn.push({
25770                 tag : 'div',
25771                 cls : 'icon',
25772                 cn :[
25773                     {
25774                         tag : 'i',
25775                         cls : 'ion ' + this.icon
25776                     }
25777                 ]
25778             });
25779         }
25780         
25781         if(this.footer){
25782             var footer = {
25783                 tag : 'a',
25784                 cls : 'small-box-footer',
25785                 href : this.fhref || '#',
25786                 html : this.footer
25787             };
25788             
25789             cfg.cn.push(footer);
25790             
25791         }
25792         
25793         return  cfg;
25794     },
25795
25796     onRender : function(ct,position){
25797         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25798
25799
25800        
25801                 
25802     },
25803
25804     setHeadline: function (value)
25805     {
25806         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25807     },
25808     
25809     setFooter: function (value, href)
25810     {
25811         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25812         
25813         if(href){
25814             this.el.select('a.small-box-footer',true).first().attr('href', href);
25815         }
25816         
25817     },
25818
25819     setContent: function (value)
25820     {
25821         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25822     },
25823
25824     initEvents: function() 
25825     {   
25826         
25827     }
25828     
25829 });
25830
25831  
25832 /*
25833  * - LGPL
25834  *
25835  * TabBox
25836  * 
25837  */
25838 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25839
25840 /**
25841  * @class Roo.bootstrap.dash.TabBox
25842  * @extends Roo.bootstrap.Component
25843  * Bootstrap TabBox class
25844  * @cfg {String} title Title of the TabBox
25845  * @cfg {String} icon Icon of the TabBox
25846  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25847  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25848  * 
25849  * @constructor
25850  * Create a new TabBox
25851  * @param {Object} config The config object
25852  */
25853
25854
25855 Roo.bootstrap.dash.TabBox = function(config){
25856     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25857     this.addEvents({
25858         // raw events
25859         /**
25860          * @event addpane
25861          * When a pane is added
25862          * @param {Roo.bootstrap.dash.TabPane} pane
25863          */
25864         "addpane" : true,
25865         /**
25866          * @event activatepane
25867          * When a pane is activated
25868          * @param {Roo.bootstrap.dash.TabPane} pane
25869          */
25870         "activatepane" : true
25871         
25872          
25873     });
25874     
25875     this.panes = [];
25876 };
25877
25878 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25879
25880     title : '',
25881     icon : false,
25882     showtabs : true,
25883     tabScrollable : false,
25884     
25885     getChildContainer : function()
25886     {
25887         return this.el.select('.tab-content', true).first();
25888     },
25889     
25890     getAutoCreate : function(){
25891         
25892         var header = {
25893             tag: 'li',
25894             cls: 'pull-left header',
25895             html: this.title,
25896             cn : []
25897         };
25898         
25899         if(this.icon){
25900             header.cn.push({
25901                 tag: 'i',
25902                 cls: 'fa ' + this.icon
25903             });
25904         }
25905         
25906         var h = {
25907             tag: 'ul',
25908             cls: 'nav nav-tabs pull-right',
25909             cn: [
25910                 header
25911             ]
25912         };
25913         
25914         if(this.tabScrollable){
25915             h = {
25916                 tag: 'div',
25917                 cls: 'tab-header',
25918                 cn: [
25919                     {
25920                         tag: 'ul',
25921                         cls: 'nav nav-tabs pull-right',
25922                         cn: [
25923                             header
25924                         ]
25925                     }
25926                 ]
25927             };
25928         }
25929         
25930         var cfg = {
25931             tag: 'div',
25932             cls: 'nav-tabs-custom',
25933             cn: [
25934                 h,
25935                 {
25936                     tag: 'div',
25937                     cls: 'tab-content no-padding',
25938                     cn: []
25939                 }
25940             ]
25941         };
25942
25943         return  cfg;
25944     },
25945     initEvents : function()
25946     {
25947         //Roo.log('add add pane handler');
25948         this.on('addpane', this.onAddPane, this);
25949     },
25950      /**
25951      * Updates the box title
25952      * @param {String} html to set the title to.
25953      */
25954     setTitle : function(value)
25955     {
25956         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25957     },
25958     onAddPane : function(pane)
25959     {
25960         this.panes.push(pane);
25961         //Roo.log('addpane');
25962         //Roo.log(pane);
25963         // tabs are rendere left to right..
25964         if(!this.showtabs){
25965             return;
25966         }
25967         
25968         var ctr = this.el.select('.nav-tabs', true).first();
25969          
25970          
25971         var existing = ctr.select('.nav-tab',true);
25972         var qty = existing.getCount();;
25973         
25974         
25975         var tab = ctr.createChild({
25976             tag : 'li',
25977             cls : 'nav-tab' + (qty ? '' : ' active'),
25978             cn : [
25979                 {
25980                     tag : 'a',
25981                     href:'#',
25982                     html : pane.title
25983                 }
25984             ]
25985         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25986         pane.tab = tab;
25987         
25988         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25989         if (!qty) {
25990             pane.el.addClass('active');
25991         }
25992         
25993                 
25994     },
25995     onTabClick : function(ev,un,ob,pane)
25996     {
25997         //Roo.log('tab - prev default');
25998         ev.preventDefault();
25999         
26000         
26001         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
26002         pane.tab.addClass('active');
26003         //Roo.log(pane.title);
26004         this.getChildContainer().select('.tab-pane',true).removeClass('active');
26005         // technically we should have a deactivate event.. but maybe add later.
26006         // and it should not de-activate the selected tab...
26007         this.fireEvent('activatepane', pane);
26008         pane.el.addClass('active');
26009         pane.fireEvent('activate');
26010         
26011         
26012     },
26013     
26014     getActivePane : function()
26015     {
26016         var r = false;
26017         Roo.each(this.panes, function(p) {
26018             if(p.el.hasClass('active')){
26019                 r = p;
26020                 return false;
26021             }
26022             
26023             return;
26024         });
26025         
26026         return r;
26027     }
26028     
26029     
26030 });
26031
26032  
26033 /*
26034  * - LGPL
26035  *
26036  * Tab pane
26037  * 
26038  */
26039 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
26040 /**
26041  * @class Roo.bootstrap.TabPane
26042  * @extends Roo.bootstrap.Component
26043  * Bootstrap TabPane class
26044  * @cfg {Boolean} active (false | true) Default false
26045  * @cfg {String} title title of panel
26046
26047  * 
26048  * @constructor
26049  * Create a new TabPane
26050  * @param {Object} config The config object
26051  */
26052
26053 Roo.bootstrap.dash.TabPane = function(config){
26054     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
26055     
26056     this.addEvents({
26057         // raw events
26058         /**
26059          * @event activate
26060          * When a pane is activated
26061          * @param {Roo.bootstrap.dash.TabPane} pane
26062          */
26063         "activate" : true
26064          
26065     });
26066 };
26067
26068 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
26069     
26070     active : false,
26071     title : '',
26072     
26073     // the tabBox that this is attached to.
26074     tab : false,
26075      
26076     getAutoCreate : function() 
26077     {
26078         var cfg = {
26079             tag: 'div',
26080             cls: 'tab-pane'
26081         };
26082         
26083         if(this.active){
26084             cfg.cls += ' active';
26085         }
26086         
26087         return cfg;
26088     },
26089     initEvents  : function()
26090     {
26091         //Roo.log('trigger add pane handler');
26092         this.parent().fireEvent('addpane', this)
26093     },
26094     
26095      /**
26096      * Updates the tab title 
26097      * @param {String} html to set the title to.
26098      */
26099     setTitle: function(str)
26100     {
26101         if (!this.tab) {
26102             return;
26103         }
26104         this.title = str;
26105         this.tab.select('a', true).first().dom.innerHTML = str;
26106         
26107     }
26108     
26109     
26110     
26111 });
26112
26113  
26114
26115
26116  /*
26117  * - LGPL
26118  *
26119  * menu
26120  * 
26121  */
26122 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26123
26124 /**
26125  * @class Roo.bootstrap.menu.Menu
26126  * @extends Roo.bootstrap.Component
26127  * Bootstrap Menu class - container for Menu
26128  * @cfg {String} html Text of the menu
26129  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26130  * @cfg {String} icon Font awesome icon
26131  * @cfg {String} pos Menu align to (top | bottom) default bottom
26132  * 
26133  * 
26134  * @constructor
26135  * Create a new Menu
26136  * @param {Object} config The config object
26137  */
26138
26139
26140 Roo.bootstrap.menu.Menu = function(config){
26141     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26142     
26143     this.addEvents({
26144         /**
26145          * @event beforeshow
26146          * Fires before this menu is displayed
26147          * @param {Roo.bootstrap.menu.Menu} this
26148          */
26149         beforeshow : true,
26150         /**
26151          * @event beforehide
26152          * Fires before this menu is hidden
26153          * @param {Roo.bootstrap.menu.Menu} this
26154          */
26155         beforehide : true,
26156         /**
26157          * @event show
26158          * Fires after this menu is displayed
26159          * @param {Roo.bootstrap.menu.Menu} this
26160          */
26161         show : true,
26162         /**
26163          * @event hide
26164          * Fires after this menu is hidden
26165          * @param {Roo.bootstrap.menu.Menu} this
26166          */
26167         hide : true,
26168         /**
26169          * @event click
26170          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26171          * @param {Roo.bootstrap.menu.Menu} this
26172          * @param {Roo.EventObject} e
26173          */
26174         click : true
26175     });
26176     
26177 };
26178
26179 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26180     
26181     submenu : false,
26182     html : '',
26183     weight : 'default',
26184     icon : false,
26185     pos : 'bottom',
26186     
26187     
26188     getChildContainer : function() {
26189         if(this.isSubMenu){
26190             return this.el;
26191         }
26192         
26193         return this.el.select('ul.dropdown-menu', true).first();  
26194     },
26195     
26196     getAutoCreate : function()
26197     {
26198         var text = [
26199             {
26200                 tag : 'span',
26201                 cls : 'roo-menu-text',
26202                 html : this.html
26203             }
26204         ];
26205         
26206         if(this.icon){
26207             text.unshift({
26208                 tag : 'i',
26209                 cls : 'fa ' + this.icon
26210             })
26211         }
26212         
26213         
26214         var cfg = {
26215             tag : 'div',
26216             cls : 'btn-group',
26217             cn : [
26218                 {
26219                     tag : 'button',
26220                     cls : 'dropdown-button btn btn-' + this.weight,
26221                     cn : text
26222                 },
26223                 {
26224                     tag : 'button',
26225                     cls : 'dropdown-toggle btn btn-' + this.weight,
26226                     cn : [
26227                         {
26228                             tag : 'span',
26229                             cls : 'caret'
26230                         }
26231                     ]
26232                 },
26233                 {
26234                     tag : 'ul',
26235                     cls : 'dropdown-menu'
26236                 }
26237             ]
26238             
26239         };
26240         
26241         if(this.pos == 'top'){
26242             cfg.cls += ' dropup';
26243         }
26244         
26245         if(this.isSubMenu){
26246             cfg = {
26247                 tag : 'ul',
26248                 cls : 'dropdown-menu'
26249             }
26250         }
26251         
26252         return cfg;
26253     },
26254     
26255     onRender : function(ct, position)
26256     {
26257         this.isSubMenu = ct.hasClass('dropdown-submenu');
26258         
26259         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26260     },
26261     
26262     initEvents : function() 
26263     {
26264         if(this.isSubMenu){
26265             return;
26266         }
26267         
26268         this.hidden = true;
26269         
26270         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26271         this.triggerEl.on('click', this.onTriggerPress, this);
26272         
26273         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26274         this.buttonEl.on('click', this.onClick, this);
26275         
26276     },
26277     
26278     list : function()
26279     {
26280         if(this.isSubMenu){
26281             return this.el;
26282         }
26283         
26284         return this.el.select('ul.dropdown-menu', true).first();
26285     },
26286     
26287     onClick : function(e)
26288     {
26289         this.fireEvent("click", this, e);
26290     },
26291     
26292     onTriggerPress  : function(e)
26293     {   
26294         if (this.isVisible()) {
26295             this.hide();
26296         } else {
26297             this.show();
26298         }
26299     },
26300     
26301     isVisible : function(){
26302         return !this.hidden;
26303     },
26304     
26305     show : function()
26306     {
26307         this.fireEvent("beforeshow", this);
26308         
26309         this.hidden = false;
26310         this.el.addClass('open');
26311         
26312         Roo.get(document).on("mouseup", this.onMouseUp, this);
26313         
26314         this.fireEvent("show", this);
26315         
26316         
26317     },
26318     
26319     hide : function()
26320     {
26321         this.fireEvent("beforehide", this);
26322         
26323         this.hidden = true;
26324         this.el.removeClass('open');
26325         
26326         Roo.get(document).un("mouseup", this.onMouseUp);
26327         
26328         this.fireEvent("hide", this);
26329     },
26330     
26331     onMouseUp : function()
26332     {
26333         this.hide();
26334     }
26335     
26336 });
26337
26338  
26339  /*
26340  * - LGPL
26341  *
26342  * menu item
26343  * 
26344  */
26345 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26346
26347 /**
26348  * @class Roo.bootstrap.menu.Item
26349  * @extends Roo.bootstrap.Component
26350  * Bootstrap MenuItem class
26351  * @cfg {Boolean} submenu (true | false) default false
26352  * @cfg {String} html text of the item
26353  * @cfg {String} href the link
26354  * @cfg {Boolean} disable (true | false) default false
26355  * @cfg {Boolean} preventDefault (true | false) default true
26356  * @cfg {String} icon Font awesome icon
26357  * @cfg {String} pos Submenu align to (left | right) default right 
26358  * 
26359  * 
26360  * @constructor
26361  * Create a new Item
26362  * @param {Object} config The config object
26363  */
26364
26365
26366 Roo.bootstrap.menu.Item = function(config){
26367     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26368     this.addEvents({
26369         /**
26370          * @event mouseover
26371          * Fires when the mouse is hovering over this menu
26372          * @param {Roo.bootstrap.menu.Item} this
26373          * @param {Roo.EventObject} e
26374          */
26375         mouseover : true,
26376         /**
26377          * @event mouseout
26378          * Fires when the mouse exits this menu
26379          * @param {Roo.bootstrap.menu.Item} this
26380          * @param {Roo.EventObject} e
26381          */
26382         mouseout : true,
26383         // raw events
26384         /**
26385          * @event click
26386          * The raw click event for the entire grid.
26387          * @param {Roo.EventObject} e
26388          */
26389         click : true
26390     });
26391 };
26392
26393 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26394     
26395     submenu : false,
26396     href : '',
26397     html : '',
26398     preventDefault: true,
26399     disable : false,
26400     icon : false,
26401     pos : 'right',
26402     
26403     getAutoCreate : function()
26404     {
26405         var text = [
26406             {
26407                 tag : 'span',
26408                 cls : 'roo-menu-item-text',
26409                 html : this.html
26410             }
26411         ];
26412         
26413         if(this.icon){
26414             text.unshift({
26415                 tag : 'i',
26416                 cls : 'fa ' + this.icon
26417             })
26418         }
26419         
26420         var cfg = {
26421             tag : 'li',
26422             cn : [
26423                 {
26424                     tag : 'a',
26425                     href : this.href || '#',
26426                     cn : text
26427                 }
26428             ]
26429         };
26430         
26431         if(this.disable){
26432             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26433         }
26434         
26435         if(this.submenu){
26436             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26437             
26438             if(this.pos == 'left'){
26439                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26440             }
26441         }
26442         
26443         return cfg;
26444     },
26445     
26446     initEvents : function() 
26447     {
26448         this.el.on('mouseover', this.onMouseOver, this);
26449         this.el.on('mouseout', this.onMouseOut, this);
26450         
26451         this.el.select('a', true).first().on('click', this.onClick, this);
26452         
26453     },
26454     
26455     onClick : function(e)
26456     {
26457         if(this.preventDefault){
26458             e.preventDefault();
26459         }
26460         
26461         this.fireEvent("click", this, e);
26462     },
26463     
26464     onMouseOver : function(e)
26465     {
26466         if(this.submenu && this.pos == 'left'){
26467             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26468         }
26469         
26470         this.fireEvent("mouseover", this, e);
26471     },
26472     
26473     onMouseOut : function(e)
26474     {
26475         this.fireEvent("mouseout", this, e);
26476     }
26477 });
26478
26479  
26480
26481  /*
26482  * - LGPL
26483  *
26484  * menu separator
26485  * 
26486  */
26487 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26488
26489 /**
26490  * @class Roo.bootstrap.menu.Separator
26491  * @extends Roo.bootstrap.Component
26492  * Bootstrap Separator class
26493  * 
26494  * @constructor
26495  * Create a new Separator
26496  * @param {Object} config The config object
26497  */
26498
26499
26500 Roo.bootstrap.menu.Separator = function(config){
26501     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26502 };
26503
26504 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26505     
26506     getAutoCreate : function(){
26507         var cfg = {
26508             tag : 'li',
26509             cls: 'divider'
26510         };
26511         
26512         return cfg;
26513     }
26514    
26515 });
26516
26517  
26518
26519  /*
26520  * - LGPL
26521  *
26522  * Tooltip
26523  * 
26524  */
26525
26526 /**
26527  * @class Roo.bootstrap.Tooltip
26528  * Bootstrap Tooltip class
26529  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26530  * to determine which dom element triggers the tooltip.
26531  * 
26532  * It needs to add support for additional attributes like tooltip-position
26533  * 
26534  * @constructor
26535  * Create a new Toolti
26536  * @param {Object} config The config object
26537  */
26538
26539 Roo.bootstrap.Tooltip = function(config){
26540     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26541     
26542     this.alignment = Roo.bootstrap.Tooltip.alignment;
26543     
26544     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26545         this.alignment = config.alignment;
26546     }
26547     
26548 };
26549
26550 Roo.apply(Roo.bootstrap.Tooltip, {
26551     /**
26552      * @function init initialize tooltip monitoring.
26553      * @static
26554      */
26555     currentEl : false,
26556     currentTip : false,
26557     currentRegion : false,
26558     
26559     //  init : delay?
26560     
26561     init : function()
26562     {
26563         Roo.get(document).on('mouseover', this.enter ,this);
26564         Roo.get(document).on('mouseout', this.leave, this);
26565          
26566         
26567         this.currentTip = new Roo.bootstrap.Tooltip();
26568     },
26569     
26570     enter : function(ev)
26571     {
26572         var dom = ev.getTarget();
26573         
26574         //Roo.log(['enter',dom]);
26575         var el = Roo.fly(dom);
26576         if (this.currentEl) {
26577             //Roo.log(dom);
26578             //Roo.log(this.currentEl);
26579             //Roo.log(this.currentEl.contains(dom));
26580             if (this.currentEl == el) {
26581                 return;
26582             }
26583             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26584                 return;
26585             }
26586
26587         }
26588         
26589         if (this.currentTip.el) {
26590             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26591         }    
26592         //Roo.log(ev);
26593         
26594         if(!el || el.dom == document){
26595             return;
26596         }
26597         
26598         var bindEl = el;
26599         
26600         // you can not look for children, as if el is the body.. then everythign is the child..
26601         if (!el.attr('tooltip')) { //
26602             if (!el.select("[tooltip]").elements.length) {
26603                 return;
26604             }
26605             // is the mouse over this child...?
26606             bindEl = el.select("[tooltip]").first();
26607             var xy = ev.getXY();
26608             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26609                 //Roo.log("not in region.");
26610                 return;
26611             }
26612             //Roo.log("child element over..");
26613             
26614         }
26615         this.currentEl = bindEl;
26616         this.currentTip.bind(bindEl);
26617         this.currentRegion = Roo.lib.Region.getRegion(dom);
26618         this.currentTip.enter();
26619         
26620     },
26621     leave : function(ev)
26622     {
26623         var dom = ev.getTarget();
26624         //Roo.log(['leave',dom]);
26625         if (!this.currentEl) {
26626             return;
26627         }
26628         
26629         
26630         if (dom != this.currentEl.dom) {
26631             return;
26632         }
26633         var xy = ev.getXY();
26634         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26635             return;
26636         }
26637         // only activate leave if mouse cursor is outside... bounding box..
26638         
26639         
26640         
26641         
26642         if (this.currentTip) {
26643             this.currentTip.leave();
26644         }
26645         //Roo.log('clear currentEl');
26646         this.currentEl = false;
26647         
26648         
26649     },
26650     alignment : {
26651         'left' : ['r-l', [-2,0], 'right'],
26652         'right' : ['l-r', [2,0], 'left'],
26653         'bottom' : ['t-b', [0,2], 'top'],
26654         'top' : [ 'b-t', [0,-2], 'bottom']
26655     }
26656     
26657 });
26658
26659
26660 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26661     
26662     
26663     bindEl : false,
26664     
26665     delay : null, // can be { show : 300 , hide: 500}
26666     
26667     timeout : null,
26668     
26669     hoverState : null, //???
26670     
26671     placement : 'bottom', 
26672     
26673     alignment : false,
26674     
26675     getAutoCreate : function(){
26676     
26677         var cfg = {
26678            cls : 'tooltip',
26679            role : 'tooltip',
26680            cn : [
26681                 {
26682                     cls : 'tooltip-arrow'
26683                 },
26684                 {
26685                     cls : 'tooltip-inner'
26686                 }
26687            ]
26688         };
26689         
26690         return cfg;
26691     },
26692     bind : function(el)
26693     {
26694         this.bindEl = el;
26695     },
26696       
26697     
26698     enter : function () {
26699        
26700         if (this.timeout != null) {
26701             clearTimeout(this.timeout);
26702         }
26703         
26704         this.hoverState = 'in';
26705          //Roo.log("enter - show");
26706         if (!this.delay || !this.delay.show) {
26707             this.show();
26708             return;
26709         }
26710         var _t = this;
26711         this.timeout = setTimeout(function () {
26712             if (_t.hoverState == 'in') {
26713                 _t.show();
26714             }
26715         }, this.delay.show);
26716     },
26717     leave : function()
26718     {
26719         clearTimeout(this.timeout);
26720     
26721         this.hoverState = 'out';
26722          if (!this.delay || !this.delay.hide) {
26723             this.hide();
26724             return;
26725         }
26726        
26727         var _t = this;
26728         this.timeout = setTimeout(function () {
26729             //Roo.log("leave - timeout");
26730             
26731             if (_t.hoverState == 'out') {
26732                 _t.hide();
26733                 Roo.bootstrap.Tooltip.currentEl = false;
26734             }
26735         }, delay);
26736     },
26737     
26738     show : function (msg)
26739     {
26740         if (!this.el) {
26741             this.render(document.body);
26742         }
26743         // set content.
26744         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26745         
26746         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26747         
26748         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26749         
26750         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26751         
26752         var placement = typeof this.placement == 'function' ?
26753             this.placement.call(this, this.el, on_el) :
26754             this.placement;
26755             
26756         var autoToken = /\s?auto?\s?/i;
26757         var autoPlace = autoToken.test(placement);
26758         if (autoPlace) {
26759             placement = placement.replace(autoToken, '') || 'top';
26760         }
26761         
26762         //this.el.detach()
26763         //this.el.setXY([0,0]);
26764         this.el.show();
26765         //this.el.dom.style.display='block';
26766         
26767         //this.el.appendTo(on_el);
26768         
26769         var p = this.getPosition();
26770         var box = this.el.getBox();
26771         
26772         if (autoPlace) {
26773             // fixme..
26774         }
26775         
26776         var align = this.alignment[placement];
26777         
26778         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26779         
26780         if(placement == 'top' || placement == 'bottom'){
26781             if(xy[0] < 0){
26782                 placement = 'right';
26783             }
26784             
26785             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26786                 placement = 'left';
26787             }
26788             
26789             var scroll = Roo.select('body', true).first().getScroll();
26790             
26791             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26792                 placement = 'top';
26793             }
26794             
26795             align = this.alignment[placement];
26796         }
26797         
26798         this.el.alignTo(this.bindEl, align[0],align[1]);
26799         //var arrow = this.el.select('.arrow',true).first();
26800         //arrow.set(align[2], 
26801         
26802         this.el.addClass(placement);
26803         
26804         this.el.addClass('in fade');
26805         
26806         this.hoverState = null;
26807         
26808         if (this.el.hasClass('fade')) {
26809             // fade it?
26810         }
26811         
26812     },
26813     hide : function()
26814     {
26815          
26816         if (!this.el) {
26817             return;
26818         }
26819         //this.el.setXY([0,0]);
26820         this.el.removeClass('in');
26821         //this.el.hide();
26822         
26823     }
26824     
26825 });
26826  
26827
26828  /*
26829  * - LGPL
26830  *
26831  * Location Picker
26832  * 
26833  */
26834
26835 /**
26836  * @class Roo.bootstrap.LocationPicker
26837  * @extends Roo.bootstrap.Component
26838  * Bootstrap LocationPicker class
26839  * @cfg {Number} latitude Position when init default 0
26840  * @cfg {Number} longitude Position when init default 0
26841  * @cfg {Number} zoom default 15
26842  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26843  * @cfg {Boolean} mapTypeControl default false
26844  * @cfg {Boolean} disableDoubleClickZoom default false
26845  * @cfg {Boolean} scrollwheel default true
26846  * @cfg {Boolean} streetViewControl default false
26847  * @cfg {Number} radius default 0
26848  * @cfg {String} locationName
26849  * @cfg {Boolean} draggable default true
26850  * @cfg {Boolean} enableAutocomplete default false
26851  * @cfg {Boolean} enableReverseGeocode default true
26852  * @cfg {String} markerTitle
26853  * 
26854  * @constructor
26855  * Create a new LocationPicker
26856  * @param {Object} config The config object
26857  */
26858
26859
26860 Roo.bootstrap.LocationPicker = function(config){
26861     
26862     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26863     
26864     this.addEvents({
26865         /**
26866          * @event initial
26867          * Fires when the picker initialized.
26868          * @param {Roo.bootstrap.LocationPicker} this
26869          * @param {Google Location} location
26870          */
26871         initial : true,
26872         /**
26873          * @event positionchanged
26874          * Fires when the picker position changed.
26875          * @param {Roo.bootstrap.LocationPicker} this
26876          * @param {Google Location} location
26877          */
26878         positionchanged : true,
26879         /**
26880          * @event resize
26881          * Fires when the map resize.
26882          * @param {Roo.bootstrap.LocationPicker} this
26883          */
26884         resize : true,
26885         /**
26886          * @event show
26887          * Fires when the map show.
26888          * @param {Roo.bootstrap.LocationPicker} this
26889          */
26890         show : true,
26891         /**
26892          * @event hide
26893          * Fires when the map hide.
26894          * @param {Roo.bootstrap.LocationPicker} this
26895          */
26896         hide : true,
26897         /**
26898          * @event mapClick
26899          * Fires when click the map.
26900          * @param {Roo.bootstrap.LocationPicker} this
26901          * @param {Map event} e
26902          */
26903         mapClick : true,
26904         /**
26905          * @event mapRightClick
26906          * Fires when right click the map.
26907          * @param {Roo.bootstrap.LocationPicker} this
26908          * @param {Map event} e
26909          */
26910         mapRightClick : true,
26911         /**
26912          * @event markerClick
26913          * Fires when click the marker.
26914          * @param {Roo.bootstrap.LocationPicker} this
26915          * @param {Map event} e
26916          */
26917         markerClick : true,
26918         /**
26919          * @event markerRightClick
26920          * Fires when right click the marker.
26921          * @param {Roo.bootstrap.LocationPicker} this
26922          * @param {Map event} e
26923          */
26924         markerRightClick : true,
26925         /**
26926          * @event OverlayViewDraw
26927          * Fires when OverlayView Draw
26928          * @param {Roo.bootstrap.LocationPicker} this
26929          */
26930         OverlayViewDraw : true,
26931         /**
26932          * @event OverlayViewOnAdd
26933          * Fires when OverlayView Draw
26934          * @param {Roo.bootstrap.LocationPicker} this
26935          */
26936         OverlayViewOnAdd : true,
26937         /**
26938          * @event OverlayViewOnRemove
26939          * Fires when OverlayView Draw
26940          * @param {Roo.bootstrap.LocationPicker} this
26941          */
26942         OverlayViewOnRemove : true,
26943         /**
26944          * @event OverlayViewShow
26945          * Fires when OverlayView Draw
26946          * @param {Roo.bootstrap.LocationPicker} this
26947          * @param {Pixel} cpx
26948          */
26949         OverlayViewShow : true,
26950         /**
26951          * @event OverlayViewHide
26952          * Fires when OverlayView Draw
26953          * @param {Roo.bootstrap.LocationPicker} this
26954          */
26955         OverlayViewHide : true,
26956         /**
26957          * @event loadexception
26958          * Fires when load google lib failed.
26959          * @param {Roo.bootstrap.LocationPicker} this
26960          */
26961         loadexception : true
26962     });
26963         
26964 };
26965
26966 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26967     
26968     gMapContext: false,
26969     
26970     latitude: 0,
26971     longitude: 0,
26972     zoom: 15,
26973     mapTypeId: false,
26974     mapTypeControl: false,
26975     disableDoubleClickZoom: false,
26976     scrollwheel: true,
26977     streetViewControl: false,
26978     radius: 0,
26979     locationName: '',
26980     draggable: true,
26981     enableAutocomplete: false,
26982     enableReverseGeocode: true,
26983     markerTitle: '',
26984     
26985     getAutoCreate: function()
26986     {
26987
26988         var cfg = {
26989             tag: 'div',
26990             cls: 'roo-location-picker'
26991         };
26992         
26993         return cfg
26994     },
26995     
26996     initEvents: function(ct, position)
26997     {       
26998         if(!this.el.getWidth() || this.isApplied()){
26999             return;
27000         }
27001         
27002         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27003         
27004         this.initial();
27005     },
27006     
27007     initial: function()
27008     {
27009         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
27010             this.fireEvent('loadexception', this);
27011             return;
27012         }
27013         
27014         if(!this.mapTypeId){
27015             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
27016         }
27017         
27018         this.gMapContext = this.GMapContext();
27019         
27020         this.initOverlayView();
27021         
27022         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
27023         
27024         var _this = this;
27025                 
27026         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
27027             _this.setPosition(_this.gMapContext.marker.position);
27028         });
27029         
27030         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
27031             _this.fireEvent('mapClick', this, event);
27032             
27033         });
27034
27035         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
27036             _this.fireEvent('mapRightClick', this, event);
27037             
27038         });
27039         
27040         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
27041             _this.fireEvent('markerClick', this, event);
27042             
27043         });
27044
27045         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
27046             _this.fireEvent('markerRightClick', this, event);
27047             
27048         });
27049         
27050         this.setPosition(this.gMapContext.location);
27051         
27052         this.fireEvent('initial', this, this.gMapContext.location);
27053     },
27054     
27055     initOverlayView: function()
27056     {
27057         var _this = this;
27058         
27059         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
27060             
27061             draw: function()
27062             {
27063                 _this.fireEvent('OverlayViewDraw', _this);
27064             },
27065             
27066             onAdd: function()
27067             {
27068                 _this.fireEvent('OverlayViewOnAdd', _this);
27069             },
27070             
27071             onRemove: function()
27072             {
27073                 _this.fireEvent('OverlayViewOnRemove', _this);
27074             },
27075             
27076             show: function(cpx)
27077             {
27078                 _this.fireEvent('OverlayViewShow', _this, cpx);
27079             },
27080             
27081             hide: function()
27082             {
27083                 _this.fireEvent('OverlayViewHide', _this);
27084             }
27085             
27086         });
27087     },
27088     
27089     fromLatLngToContainerPixel: function(event)
27090     {
27091         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
27092     },
27093     
27094     isApplied: function() 
27095     {
27096         return this.getGmapContext() == false ? false : true;
27097     },
27098     
27099     getGmapContext: function() 
27100     {
27101         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27102     },
27103     
27104     GMapContext: function() 
27105     {
27106         var position = new google.maps.LatLng(this.latitude, this.longitude);
27107         
27108         var _map = new google.maps.Map(this.el.dom, {
27109             center: position,
27110             zoom: this.zoom,
27111             mapTypeId: this.mapTypeId,
27112             mapTypeControl: this.mapTypeControl,
27113             disableDoubleClickZoom: this.disableDoubleClickZoom,
27114             scrollwheel: this.scrollwheel,
27115             streetViewControl: this.streetViewControl,
27116             locationName: this.locationName,
27117             draggable: this.draggable,
27118             enableAutocomplete: this.enableAutocomplete,
27119             enableReverseGeocode: this.enableReverseGeocode
27120         });
27121         
27122         var _marker = new google.maps.Marker({
27123             position: position,
27124             map: _map,
27125             title: this.markerTitle,
27126             draggable: this.draggable
27127         });
27128         
27129         return {
27130             map: _map,
27131             marker: _marker,
27132             circle: null,
27133             location: position,
27134             radius: this.radius,
27135             locationName: this.locationName,
27136             addressComponents: {
27137                 formatted_address: null,
27138                 addressLine1: null,
27139                 addressLine2: null,
27140                 streetName: null,
27141                 streetNumber: null,
27142                 city: null,
27143                 district: null,
27144                 state: null,
27145                 stateOrProvince: null
27146             },
27147             settings: this,
27148             domContainer: this.el.dom,
27149             geodecoder: new google.maps.Geocoder()
27150         };
27151     },
27152     
27153     drawCircle: function(center, radius, options) 
27154     {
27155         if (this.gMapContext.circle != null) {
27156             this.gMapContext.circle.setMap(null);
27157         }
27158         if (radius > 0) {
27159             radius *= 1;
27160             options = Roo.apply({}, options, {
27161                 strokeColor: "#0000FF",
27162                 strokeOpacity: .35,
27163                 strokeWeight: 2,
27164                 fillColor: "#0000FF",
27165                 fillOpacity: .2
27166             });
27167             
27168             options.map = this.gMapContext.map;
27169             options.radius = radius;
27170             options.center = center;
27171             this.gMapContext.circle = new google.maps.Circle(options);
27172             return this.gMapContext.circle;
27173         }
27174         
27175         return null;
27176     },
27177     
27178     setPosition: function(location) 
27179     {
27180         this.gMapContext.location = location;
27181         this.gMapContext.marker.setPosition(location);
27182         this.gMapContext.map.panTo(location);
27183         this.drawCircle(location, this.gMapContext.radius, {});
27184         
27185         var _this = this;
27186         
27187         if (this.gMapContext.settings.enableReverseGeocode) {
27188             this.gMapContext.geodecoder.geocode({
27189                 latLng: this.gMapContext.location
27190             }, function(results, status) {
27191                 
27192                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27193                     _this.gMapContext.locationName = results[0].formatted_address;
27194                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27195                     
27196                     _this.fireEvent('positionchanged', this, location);
27197                 }
27198             });
27199             
27200             return;
27201         }
27202         
27203         this.fireEvent('positionchanged', this, location);
27204     },
27205     
27206     resize: function()
27207     {
27208         google.maps.event.trigger(this.gMapContext.map, "resize");
27209         
27210         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27211         
27212         this.fireEvent('resize', this);
27213     },
27214     
27215     setPositionByLatLng: function(latitude, longitude)
27216     {
27217         this.setPosition(new google.maps.LatLng(latitude, longitude));
27218     },
27219     
27220     getCurrentPosition: function() 
27221     {
27222         return {
27223             latitude: this.gMapContext.location.lat(),
27224             longitude: this.gMapContext.location.lng()
27225         };
27226     },
27227     
27228     getAddressName: function() 
27229     {
27230         return this.gMapContext.locationName;
27231     },
27232     
27233     getAddressComponents: function() 
27234     {
27235         return this.gMapContext.addressComponents;
27236     },
27237     
27238     address_component_from_google_geocode: function(address_components) 
27239     {
27240         var result = {};
27241         
27242         for (var i = 0; i < address_components.length; i++) {
27243             var component = address_components[i];
27244             if (component.types.indexOf("postal_code") >= 0) {
27245                 result.postalCode = component.short_name;
27246             } else if (component.types.indexOf("street_number") >= 0) {
27247                 result.streetNumber = component.short_name;
27248             } else if (component.types.indexOf("route") >= 0) {
27249                 result.streetName = component.short_name;
27250             } else if (component.types.indexOf("neighborhood") >= 0) {
27251                 result.city = component.short_name;
27252             } else if (component.types.indexOf("locality") >= 0) {
27253                 result.city = component.short_name;
27254             } else if (component.types.indexOf("sublocality") >= 0) {
27255                 result.district = component.short_name;
27256             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27257                 result.stateOrProvince = component.short_name;
27258             } else if (component.types.indexOf("country") >= 0) {
27259                 result.country = component.short_name;
27260             }
27261         }
27262         
27263         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27264         result.addressLine2 = "";
27265         return result;
27266     },
27267     
27268     setZoomLevel: function(zoom)
27269     {
27270         this.gMapContext.map.setZoom(zoom);
27271     },
27272     
27273     show: function()
27274     {
27275         if(!this.el){
27276             return;
27277         }
27278         
27279         this.el.show();
27280         
27281         this.resize();
27282         
27283         this.fireEvent('show', this);
27284     },
27285     
27286     hide: function()
27287     {
27288         if(!this.el){
27289             return;
27290         }
27291         
27292         this.el.hide();
27293         
27294         this.fireEvent('hide', this);
27295     }
27296     
27297 });
27298
27299 Roo.apply(Roo.bootstrap.LocationPicker, {
27300     
27301     OverlayView : function(map, options)
27302     {
27303         options = options || {};
27304         
27305         this.setMap(map);
27306     }
27307     
27308     
27309 });/**
27310  * @class Roo.bootstrap.Alert
27311  * @extends Roo.bootstrap.Component
27312  * Bootstrap Alert class - shows an alert area box
27313  * eg
27314  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
27315   Enter a valid email address
27316 </div>
27317  * @licence LGPL
27318  * @cfg {String} title The title of alert
27319  * @cfg {String} html The content of alert
27320  * @cfg {String} weight (  success | info | warning | danger )
27321  * @cfg {String} faicon font-awesomeicon
27322  * 
27323  * @constructor
27324  * Create a new alert
27325  * @param {Object} config The config object
27326  */
27327
27328
27329 Roo.bootstrap.Alert = function(config){
27330     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27331     
27332 };
27333
27334 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27335     
27336     title: '',
27337     html: '',
27338     weight: false,
27339     faicon: false,
27340     
27341     getAutoCreate : function()
27342     {
27343         
27344         var cfg = {
27345             tag : 'div',
27346             cls : 'alert',
27347             cn : [
27348                 {
27349                     tag : 'i',
27350                     cls : 'roo-alert-icon'
27351                     
27352                 },
27353                 {
27354                     tag : 'b',
27355                     cls : 'roo-alert-title',
27356                     html : this.title
27357                 },
27358                 {
27359                     tag : 'span',
27360                     cls : 'roo-alert-text',
27361                     html : this.html
27362                 }
27363             ]
27364         };
27365         
27366         if(this.faicon){
27367             cfg.cn[0].cls += ' fa ' + this.faicon;
27368         }
27369         
27370         if(this.weight){
27371             cfg.cls += ' alert-' + this.weight;
27372         }
27373         
27374         return cfg;
27375     },
27376     
27377     initEvents: function() 
27378     {
27379         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27380     },
27381     
27382     setTitle : function(str)
27383     {
27384         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27385     },
27386     
27387     setText : function(str)
27388     {
27389         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27390     },
27391     
27392     setWeight : function(weight)
27393     {
27394         if(this.weight){
27395             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27396         }
27397         
27398         this.weight = weight;
27399         
27400         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27401     },
27402     
27403     setIcon : function(icon)
27404     {
27405         if(this.faicon){
27406             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27407         }
27408         
27409         this.faicon = icon;
27410         
27411         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27412     },
27413     
27414     hide: function() 
27415     {
27416         this.el.hide();   
27417     },
27418     
27419     show: function() 
27420     {  
27421         this.el.show();   
27422     }
27423     
27424 });
27425
27426  
27427 /*
27428 * Licence: LGPL
27429 */
27430
27431 /**
27432  * @class Roo.bootstrap.UploadCropbox
27433  * @extends Roo.bootstrap.Component
27434  * Bootstrap UploadCropbox class
27435  * @cfg {String} emptyText show when image has been loaded
27436  * @cfg {String} rotateNotify show when image too small to rotate
27437  * @cfg {Number} errorTimeout default 3000
27438  * @cfg {Number} minWidth default 300
27439  * @cfg {Number} minHeight default 300
27440  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27441  * @cfg {Boolean} isDocument (true|false) default false
27442  * @cfg {String} url action url
27443  * @cfg {String} paramName default 'imageUpload'
27444  * @cfg {String} method default POST
27445  * @cfg {Boolean} loadMask (true|false) default true
27446  * @cfg {Boolean} loadingText default 'Loading...'
27447  * 
27448  * @constructor
27449  * Create a new UploadCropbox
27450  * @param {Object} config The config object
27451  */
27452
27453 Roo.bootstrap.UploadCropbox = function(config){
27454     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27455     
27456     this.addEvents({
27457         /**
27458          * @event beforeselectfile
27459          * Fire before select file
27460          * @param {Roo.bootstrap.UploadCropbox} this
27461          */
27462         "beforeselectfile" : true,
27463         /**
27464          * @event initial
27465          * Fire after initEvent
27466          * @param {Roo.bootstrap.UploadCropbox} this
27467          */
27468         "initial" : true,
27469         /**
27470          * @event crop
27471          * Fire after initEvent
27472          * @param {Roo.bootstrap.UploadCropbox} this
27473          * @param {String} data
27474          */
27475         "crop" : true,
27476         /**
27477          * @event prepare
27478          * Fire when preparing the file data
27479          * @param {Roo.bootstrap.UploadCropbox} this
27480          * @param {Object} file
27481          */
27482         "prepare" : true,
27483         /**
27484          * @event exception
27485          * Fire when get exception
27486          * @param {Roo.bootstrap.UploadCropbox} this
27487          * @param {XMLHttpRequest} xhr
27488          */
27489         "exception" : true,
27490         /**
27491          * @event beforeloadcanvas
27492          * Fire before load the canvas
27493          * @param {Roo.bootstrap.UploadCropbox} this
27494          * @param {String} src
27495          */
27496         "beforeloadcanvas" : true,
27497         /**
27498          * @event trash
27499          * Fire when trash image
27500          * @param {Roo.bootstrap.UploadCropbox} this
27501          */
27502         "trash" : true,
27503         /**
27504          * @event download
27505          * Fire when download the image
27506          * @param {Roo.bootstrap.UploadCropbox} this
27507          */
27508         "download" : true,
27509         /**
27510          * @event footerbuttonclick
27511          * Fire when footerbuttonclick
27512          * @param {Roo.bootstrap.UploadCropbox} this
27513          * @param {String} type
27514          */
27515         "footerbuttonclick" : true,
27516         /**
27517          * @event resize
27518          * Fire when resize
27519          * @param {Roo.bootstrap.UploadCropbox} this
27520          */
27521         "resize" : true,
27522         /**
27523          * @event rotate
27524          * Fire when rotate the image
27525          * @param {Roo.bootstrap.UploadCropbox} this
27526          * @param {String} pos
27527          */
27528         "rotate" : true,
27529         /**
27530          * @event inspect
27531          * Fire when inspect the file
27532          * @param {Roo.bootstrap.UploadCropbox} this
27533          * @param {Object} file
27534          */
27535         "inspect" : true,
27536         /**
27537          * @event upload
27538          * Fire when xhr upload the file
27539          * @param {Roo.bootstrap.UploadCropbox} this
27540          * @param {Object} data
27541          */
27542         "upload" : true,
27543         /**
27544          * @event arrange
27545          * Fire when arrange the file data
27546          * @param {Roo.bootstrap.UploadCropbox} this
27547          * @param {Object} formData
27548          */
27549         "arrange" : true
27550     });
27551     
27552     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27553 };
27554
27555 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27556     
27557     emptyText : 'Click to upload image',
27558     rotateNotify : 'Image is too small to rotate',
27559     errorTimeout : 3000,
27560     scale : 0,
27561     baseScale : 1,
27562     rotate : 0,
27563     dragable : false,
27564     pinching : false,
27565     mouseX : 0,
27566     mouseY : 0,
27567     cropData : false,
27568     minWidth : 300,
27569     minHeight : 300,
27570     file : false,
27571     exif : {},
27572     baseRotate : 1,
27573     cropType : 'image/jpeg',
27574     buttons : false,
27575     canvasLoaded : false,
27576     isDocument : false,
27577     method : 'POST',
27578     paramName : 'imageUpload',
27579     loadMask : true,
27580     loadingText : 'Loading...',
27581     maskEl : false,
27582     
27583     getAutoCreate : function()
27584     {
27585         var cfg = {
27586             tag : 'div',
27587             cls : 'roo-upload-cropbox',
27588             cn : [
27589                 {
27590                     tag : 'input',
27591                     cls : 'roo-upload-cropbox-selector',
27592                     type : 'file'
27593                 },
27594                 {
27595                     tag : 'div',
27596                     cls : 'roo-upload-cropbox-body',
27597                     style : 'cursor:pointer',
27598                     cn : [
27599                         {
27600                             tag : 'div',
27601                             cls : 'roo-upload-cropbox-preview'
27602                         },
27603                         {
27604                             tag : 'div',
27605                             cls : 'roo-upload-cropbox-thumb'
27606                         },
27607                         {
27608                             tag : 'div',
27609                             cls : 'roo-upload-cropbox-empty-notify',
27610                             html : this.emptyText
27611                         },
27612                         {
27613                             tag : 'div',
27614                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27615                             html : this.rotateNotify
27616                         }
27617                     ]
27618                 },
27619                 {
27620                     tag : 'div',
27621                     cls : 'roo-upload-cropbox-footer',
27622                     cn : {
27623                         tag : 'div',
27624                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27625                         cn : []
27626                     }
27627                 }
27628             ]
27629         };
27630         
27631         return cfg;
27632     },
27633     
27634     onRender : function(ct, position)
27635     {
27636         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27637         
27638         if (this.buttons.length) {
27639             
27640             Roo.each(this.buttons, function(bb) {
27641                 
27642                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27643                 
27644                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27645                 
27646             }, this);
27647         }
27648         
27649         if(this.loadMask){
27650             this.maskEl = this.el;
27651         }
27652     },
27653     
27654     initEvents : function()
27655     {
27656         this.urlAPI = (window.createObjectURL && window) || 
27657                                 (window.URL && URL.revokeObjectURL && URL) || 
27658                                 (window.webkitURL && webkitURL);
27659                         
27660         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27661         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27662         
27663         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27664         this.selectorEl.hide();
27665         
27666         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27667         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27668         
27669         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27670         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27671         this.thumbEl.hide();
27672         
27673         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27674         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27675         
27676         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27677         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27678         this.errorEl.hide();
27679         
27680         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27681         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27682         this.footerEl.hide();
27683         
27684         this.setThumbBoxSize();
27685         
27686         this.bind();
27687         
27688         this.resize();
27689         
27690         this.fireEvent('initial', this);
27691     },
27692
27693     bind : function()
27694     {
27695         var _this = this;
27696         
27697         window.addEventListener("resize", function() { _this.resize(); } );
27698         
27699         this.bodyEl.on('click', this.beforeSelectFile, this);
27700         
27701         if(Roo.isTouch){
27702             this.bodyEl.on('touchstart', this.onTouchStart, this);
27703             this.bodyEl.on('touchmove', this.onTouchMove, this);
27704             this.bodyEl.on('touchend', this.onTouchEnd, this);
27705         }
27706         
27707         if(!Roo.isTouch){
27708             this.bodyEl.on('mousedown', this.onMouseDown, this);
27709             this.bodyEl.on('mousemove', this.onMouseMove, this);
27710             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27711             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27712             Roo.get(document).on('mouseup', this.onMouseUp, this);
27713         }
27714         
27715         this.selectorEl.on('change', this.onFileSelected, this);
27716     },
27717     
27718     reset : function()
27719     {    
27720         this.scale = 0;
27721         this.baseScale = 1;
27722         this.rotate = 0;
27723         this.baseRotate = 1;
27724         this.dragable = false;
27725         this.pinching = false;
27726         this.mouseX = 0;
27727         this.mouseY = 0;
27728         this.cropData = false;
27729         this.notifyEl.dom.innerHTML = this.emptyText;
27730         
27731         this.selectorEl.dom.value = '';
27732         
27733     },
27734     
27735     resize : function()
27736     {
27737         if(this.fireEvent('resize', this) != false){
27738             this.setThumbBoxPosition();
27739             this.setCanvasPosition();
27740         }
27741     },
27742     
27743     onFooterButtonClick : function(e, el, o, type)
27744     {
27745         switch (type) {
27746             case 'rotate-left' :
27747                 this.onRotateLeft(e);
27748                 break;
27749             case 'rotate-right' :
27750                 this.onRotateRight(e);
27751                 break;
27752             case 'picture' :
27753                 this.beforeSelectFile(e);
27754                 break;
27755             case 'trash' :
27756                 this.trash(e);
27757                 break;
27758             case 'crop' :
27759                 this.crop(e);
27760                 break;
27761             case 'download' :
27762                 this.download(e);
27763                 break;
27764             default :
27765                 break;
27766         }
27767         
27768         this.fireEvent('footerbuttonclick', this, type);
27769     },
27770     
27771     beforeSelectFile : function(e)
27772     {
27773         e.preventDefault();
27774         
27775         if(this.fireEvent('beforeselectfile', this) != false){
27776             this.selectorEl.dom.click();
27777         }
27778     },
27779     
27780     onFileSelected : function(e)
27781     {
27782         e.preventDefault();
27783         
27784         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27785             return;
27786         }
27787         
27788         var file = this.selectorEl.dom.files[0];
27789         
27790         if(this.fireEvent('inspect', this, file) != false){
27791             this.prepare(file);
27792         }
27793         
27794     },
27795     
27796     trash : function(e)
27797     {
27798         this.fireEvent('trash', this);
27799     },
27800     
27801     download : function(e)
27802     {
27803         this.fireEvent('download', this);
27804     },
27805     
27806     loadCanvas : function(src)
27807     {   
27808         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27809             
27810             this.reset();
27811             
27812             this.imageEl = document.createElement('img');
27813             
27814             var _this = this;
27815             
27816             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27817             
27818             this.imageEl.src = src;
27819         }
27820     },
27821     
27822     onLoadCanvas : function()
27823     {   
27824         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27825         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27826         
27827         this.bodyEl.un('click', this.beforeSelectFile, this);
27828         
27829         this.notifyEl.hide();
27830         this.thumbEl.show();
27831         this.footerEl.show();
27832         
27833         this.baseRotateLevel();
27834         
27835         if(this.isDocument){
27836             this.setThumbBoxSize();
27837         }
27838         
27839         this.setThumbBoxPosition();
27840         
27841         this.baseScaleLevel();
27842         
27843         this.draw();
27844         
27845         this.resize();
27846         
27847         this.canvasLoaded = true;
27848         
27849         if(this.loadMask){
27850             this.maskEl.unmask();
27851         }
27852         
27853     },
27854     
27855     setCanvasPosition : function()
27856     {   
27857         if(!this.canvasEl){
27858             return;
27859         }
27860         
27861         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27862         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27863         
27864         this.previewEl.setLeft(pw);
27865         this.previewEl.setTop(ph);
27866         
27867     },
27868     
27869     onMouseDown : function(e)
27870     {   
27871         e.stopEvent();
27872         
27873         this.dragable = true;
27874         this.pinching = false;
27875         
27876         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27877             this.dragable = false;
27878             return;
27879         }
27880         
27881         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27882         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27883         
27884     },
27885     
27886     onMouseMove : function(e)
27887     {   
27888         e.stopEvent();
27889         
27890         if(!this.canvasLoaded){
27891             return;
27892         }
27893         
27894         if (!this.dragable){
27895             return;
27896         }
27897         
27898         var minX = Math.ceil(this.thumbEl.getLeft(true));
27899         var minY = Math.ceil(this.thumbEl.getTop(true));
27900         
27901         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27902         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27903         
27904         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27905         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27906         
27907         x = x - this.mouseX;
27908         y = y - this.mouseY;
27909         
27910         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27911         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27912         
27913         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27914         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27915         
27916         this.previewEl.setLeft(bgX);
27917         this.previewEl.setTop(bgY);
27918         
27919         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27920         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27921     },
27922     
27923     onMouseUp : function(e)
27924     {   
27925         e.stopEvent();
27926         
27927         this.dragable = false;
27928     },
27929     
27930     onMouseWheel : function(e)
27931     {   
27932         e.stopEvent();
27933         
27934         this.startScale = this.scale;
27935         
27936         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27937         
27938         if(!this.zoomable()){
27939             this.scale = this.startScale;
27940             return;
27941         }
27942         
27943         this.draw();
27944         
27945         return;
27946     },
27947     
27948     zoomable : function()
27949     {
27950         var minScale = this.thumbEl.getWidth() / this.minWidth;
27951         
27952         if(this.minWidth < this.minHeight){
27953             minScale = this.thumbEl.getHeight() / this.minHeight;
27954         }
27955         
27956         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27957         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27958         
27959         if(
27960                 this.isDocument &&
27961                 (this.rotate == 0 || this.rotate == 180) && 
27962                 (
27963                     width > this.imageEl.OriginWidth || 
27964                     height > this.imageEl.OriginHeight ||
27965                     (width < this.minWidth && height < this.minHeight)
27966                 )
27967         ){
27968             return false;
27969         }
27970         
27971         if(
27972                 this.isDocument &&
27973                 (this.rotate == 90 || this.rotate == 270) && 
27974                 (
27975                     width > this.imageEl.OriginWidth || 
27976                     height > this.imageEl.OriginHeight ||
27977                     (width < this.minHeight && height < this.minWidth)
27978                 )
27979         ){
27980             return false;
27981         }
27982         
27983         if(
27984                 !this.isDocument &&
27985                 (this.rotate == 0 || this.rotate == 180) && 
27986                 (
27987                     width < this.minWidth || 
27988                     width > this.imageEl.OriginWidth || 
27989                     height < this.minHeight || 
27990                     height > this.imageEl.OriginHeight
27991                 )
27992         ){
27993             return false;
27994         }
27995         
27996         if(
27997                 !this.isDocument &&
27998                 (this.rotate == 90 || this.rotate == 270) && 
27999                 (
28000                     width < this.minHeight || 
28001                     width > this.imageEl.OriginWidth || 
28002                     height < this.minWidth || 
28003                     height > this.imageEl.OriginHeight
28004                 )
28005         ){
28006             return false;
28007         }
28008         
28009         return true;
28010         
28011     },
28012     
28013     onRotateLeft : function(e)
28014     {   
28015         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28016             
28017             var minScale = this.thumbEl.getWidth() / this.minWidth;
28018             
28019             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28020             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28021             
28022             this.startScale = this.scale;
28023             
28024             while (this.getScaleLevel() < minScale){
28025             
28026                 this.scale = this.scale + 1;
28027                 
28028                 if(!this.zoomable()){
28029                     break;
28030                 }
28031                 
28032                 if(
28033                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28034                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28035                 ){
28036                     continue;
28037                 }
28038                 
28039                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28040
28041                 this.draw();
28042                 
28043                 return;
28044             }
28045             
28046             this.scale = this.startScale;
28047             
28048             this.onRotateFail();
28049             
28050             return false;
28051         }
28052         
28053         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
28054
28055         if(this.isDocument){
28056             this.setThumbBoxSize();
28057             this.setThumbBoxPosition();
28058             this.setCanvasPosition();
28059         }
28060         
28061         this.draw();
28062         
28063         this.fireEvent('rotate', this, 'left');
28064         
28065     },
28066     
28067     onRotateRight : function(e)
28068     {
28069         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
28070             
28071             var minScale = this.thumbEl.getWidth() / this.minWidth;
28072         
28073             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
28074             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
28075             
28076             this.startScale = this.scale;
28077             
28078             while (this.getScaleLevel() < minScale){
28079             
28080                 this.scale = this.scale + 1;
28081                 
28082                 if(!this.zoomable()){
28083                     break;
28084                 }
28085                 
28086                 if(
28087                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
28088                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
28089                 ){
28090                     continue;
28091                 }
28092                 
28093                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28094
28095                 this.draw();
28096                 
28097                 return;
28098             }
28099             
28100             this.scale = this.startScale;
28101             
28102             this.onRotateFail();
28103             
28104             return false;
28105         }
28106         
28107         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28108
28109         if(this.isDocument){
28110             this.setThumbBoxSize();
28111             this.setThumbBoxPosition();
28112             this.setCanvasPosition();
28113         }
28114         
28115         this.draw();
28116         
28117         this.fireEvent('rotate', this, 'right');
28118     },
28119     
28120     onRotateFail : function()
28121     {
28122         this.errorEl.show(true);
28123         
28124         var _this = this;
28125         
28126         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28127     },
28128     
28129     draw : function()
28130     {
28131         this.previewEl.dom.innerHTML = '';
28132         
28133         var canvasEl = document.createElement("canvas");
28134         
28135         var contextEl = canvasEl.getContext("2d");
28136         
28137         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28138         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28139         var center = this.imageEl.OriginWidth / 2;
28140         
28141         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28142             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28143             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28144             center = this.imageEl.OriginHeight / 2;
28145         }
28146         
28147         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28148         
28149         contextEl.translate(center, center);
28150         contextEl.rotate(this.rotate * Math.PI / 180);
28151
28152         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28153         
28154         this.canvasEl = document.createElement("canvas");
28155         
28156         this.contextEl = this.canvasEl.getContext("2d");
28157         
28158         switch (this.rotate) {
28159             case 0 :
28160                 
28161                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28162                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28163                 
28164                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28165                 
28166                 break;
28167             case 90 : 
28168                 
28169                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28170                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28171                 
28172                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28173                     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);
28174                     break;
28175                 }
28176                 
28177                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28178                 
28179                 break;
28180             case 180 :
28181                 
28182                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28183                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28184                 
28185                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28186                     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);
28187                     break;
28188                 }
28189                 
28190                 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);
28191                 
28192                 break;
28193             case 270 :
28194                 
28195                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28196                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28197         
28198                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28199                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28200                     break;
28201                 }
28202                 
28203                 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);
28204                 
28205                 break;
28206             default : 
28207                 break;
28208         }
28209         
28210         this.previewEl.appendChild(this.canvasEl);
28211         
28212         this.setCanvasPosition();
28213     },
28214     
28215     crop : function()
28216     {
28217         if(!this.canvasLoaded){
28218             return;
28219         }
28220         
28221         var imageCanvas = document.createElement("canvas");
28222         
28223         var imageContext = imageCanvas.getContext("2d");
28224         
28225         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28226         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28227         
28228         var center = imageCanvas.width / 2;
28229         
28230         imageContext.translate(center, center);
28231         
28232         imageContext.rotate(this.rotate * Math.PI / 180);
28233         
28234         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28235         
28236         var canvas = document.createElement("canvas");
28237         
28238         var context = canvas.getContext("2d");
28239                 
28240         canvas.width = this.minWidth;
28241         canvas.height = this.minHeight;
28242
28243         switch (this.rotate) {
28244             case 0 :
28245                 
28246                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28247                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28248                 
28249                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28250                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28251                 
28252                 var targetWidth = this.minWidth - 2 * x;
28253                 var targetHeight = this.minHeight - 2 * y;
28254                 
28255                 var scale = 1;
28256                 
28257                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28258                     scale = targetWidth / width;
28259                 }
28260                 
28261                 if(x > 0 && y == 0){
28262                     scale = targetHeight / height;
28263                 }
28264                 
28265                 if(x > 0 && y > 0){
28266                     scale = targetWidth / width;
28267                     
28268                     if(width < height){
28269                         scale = targetHeight / height;
28270                     }
28271                 }
28272                 
28273                 context.scale(scale, scale);
28274                 
28275                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28276                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28277
28278                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28279                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28280
28281                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28282                 
28283                 break;
28284             case 90 : 
28285                 
28286                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28287                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28288                 
28289                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28290                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28291                 
28292                 var targetWidth = this.minWidth - 2 * x;
28293                 var targetHeight = this.minHeight - 2 * y;
28294                 
28295                 var scale = 1;
28296                 
28297                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28298                     scale = targetWidth / width;
28299                 }
28300                 
28301                 if(x > 0 && y == 0){
28302                     scale = targetHeight / height;
28303                 }
28304                 
28305                 if(x > 0 && y > 0){
28306                     scale = targetWidth / width;
28307                     
28308                     if(width < height){
28309                         scale = targetHeight / height;
28310                     }
28311                 }
28312                 
28313                 context.scale(scale, scale);
28314                 
28315                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28316                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28317
28318                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28319                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28320                 
28321                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28322                 
28323                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28324                 
28325                 break;
28326             case 180 :
28327                 
28328                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28329                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28330                 
28331                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28332                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28333                 
28334                 var targetWidth = this.minWidth - 2 * x;
28335                 var targetHeight = this.minHeight - 2 * y;
28336                 
28337                 var scale = 1;
28338                 
28339                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28340                     scale = targetWidth / width;
28341                 }
28342                 
28343                 if(x > 0 && y == 0){
28344                     scale = targetHeight / height;
28345                 }
28346                 
28347                 if(x > 0 && y > 0){
28348                     scale = targetWidth / width;
28349                     
28350                     if(width < height){
28351                         scale = targetHeight / height;
28352                     }
28353                 }
28354                 
28355                 context.scale(scale, scale);
28356                 
28357                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28358                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28359
28360                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28361                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28362
28363                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28364                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28365                 
28366                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28367                 
28368                 break;
28369             case 270 :
28370                 
28371                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28372                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28373                 
28374                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28375                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28376                 
28377                 var targetWidth = this.minWidth - 2 * x;
28378                 var targetHeight = this.minHeight - 2 * y;
28379                 
28380                 var scale = 1;
28381                 
28382                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28383                     scale = targetWidth / width;
28384                 }
28385                 
28386                 if(x > 0 && y == 0){
28387                     scale = targetHeight / height;
28388                 }
28389                 
28390                 if(x > 0 && y > 0){
28391                     scale = targetWidth / width;
28392                     
28393                     if(width < height){
28394                         scale = targetHeight / height;
28395                     }
28396                 }
28397                 
28398                 context.scale(scale, scale);
28399                 
28400                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28401                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28402
28403                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28404                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28405                 
28406                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28407                 
28408                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28409                 
28410                 break;
28411             default : 
28412                 break;
28413         }
28414         
28415         this.cropData = canvas.toDataURL(this.cropType);
28416         
28417         if(this.fireEvent('crop', this, this.cropData) !== false){
28418             this.process(this.file, this.cropData);
28419         }
28420         
28421         return;
28422         
28423     },
28424     
28425     setThumbBoxSize : function()
28426     {
28427         var width, height;
28428         
28429         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28430             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28431             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28432             
28433             this.minWidth = width;
28434             this.minHeight = height;
28435             
28436             if(this.rotate == 90 || this.rotate == 270){
28437                 this.minWidth = height;
28438                 this.minHeight = width;
28439             }
28440         }
28441         
28442         height = 300;
28443         width = Math.ceil(this.minWidth * height / this.minHeight);
28444         
28445         if(this.minWidth > this.minHeight){
28446             width = 300;
28447             height = Math.ceil(this.minHeight * width / this.minWidth);
28448         }
28449         
28450         this.thumbEl.setStyle({
28451             width : width + 'px',
28452             height : height + 'px'
28453         });
28454
28455         return;
28456             
28457     },
28458     
28459     setThumbBoxPosition : function()
28460     {
28461         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28462         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28463         
28464         this.thumbEl.setLeft(x);
28465         this.thumbEl.setTop(y);
28466         
28467     },
28468     
28469     baseRotateLevel : function()
28470     {
28471         this.baseRotate = 1;
28472         
28473         if(
28474                 typeof(this.exif) != 'undefined' &&
28475                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28476                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28477         ){
28478             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28479         }
28480         
28481         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28482         
28483     },
28484     
28485     baseScaleLevel : function()
28486     {
28487         var width, height;
28488         
28489         if(this.isDocument){
28490             
28491             if(this.baseRotate == 6 || this.baseRotate == 8){
28492             
28493                 height = this.thumbEl.getHeight();
28494                 this.baseScale = height / this.imageEl.OriginWidth;
28495
28496                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28497                     width = this.thumbEl.getWidth();
28498                     this.baseScale = width / this.imageEl.OriginHeight;
28499                 }
28500
28501                 return;
28502             }
28503
28504             height = this.thumbEl.getHeight();
28505             this.baseScale = height / this.imageEl.OriginHeight;
28506
28507             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28508                 width = this.thumbEl.getWidth();
28509                 this.baseScale = width / this.imageEl.OriginWidth;
28510             }
28511
28512             return;
28513         }
28514         
28515         if(this.baseRotate == 6 || this.baseRotate == 8){
28516             
28517             width = this.thumbEl.getHeight();
28518             this.baseScale = width / this.imageEl.OriginHeight;
28519             
28520             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28521                 height = this.thumbEl.getWidth();
28522                 this.baseScale = height / this.imageEl.OriginHeight;
28523             }
28524             
28525             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28526                 height = this.thumbEl.getWidth();
28527                 this.baseScale = height / this.imageEl.OriginHeight;
28528                 
28529                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28530                     width = this.thumbEl.getHeight();
28531                     this.baseScale = width / this.imageEl.OriginWidth;
28532                 }
28533             }
28534             
28535             return;
28536         }
28537         
28538         width = this.thumbEl.getWidth();
28539         this.baseScale = width / this.imageEl.OriginWidth;
28540         
28541         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28542             height = this.thumbEl.getHeight();
28543             this.baseScale = height / this.imageEl.OriginHeight;
28544         }
28545         
28546         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28547             
28548             height = this.thumbEl.getHeight();
28549             this.baseScale = height / this.imageEl.OriginHeight;
28550             
28551             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28552                 width = this.thumbEl.getWidth();
28553                 this.baseScale = width / this.imageEl.OriginWidth;
28554             }
28555             
28556         }
28557         
28558         return;
28559     },
28560     
28561     getScaleLevel : function()
28562     {
28563         return this.baseScale * Math.pow(1.1, this.scale);
28564     },
28565     
28566     onTouchStart : function(e)
28567     {
28568         if(!this.canvasLoaded){
28569             this.beforeSelectFile(e);
28570             return;
28571         }
28572         
28573         var touches = e.browserEvent.touches;
28574         
28575         if(!touches){
28576             return;
28577         }
28578         
28579         if(touches.length == 1){
28580             this.onMouseDown(e);
28581             return;
28582         }
28583         
28584         if(touches.length != 2){
28585             return;
28586         }
28587         
28588         var coords = [];
28589         
28590         for(var i = 0, finger; finger = touches[i]; i++){
28591             coords.push(finger.pageX, finger.pageY);
28592         }
28593         
28594         var x = Math.pow(coords[0] - coords[2], 2);
28595         var y = Math.pow(coords[1] - coords[3], 2);
28596         
28597         this.startDistance = Math.sqrt(x + y);
28598         
28599         this.startScale = this.scale;
28600         
28601         this.pinching = true;
28602         this.dragable = false;
28603         
28604     },
28605     
28606     onTouchMove : function(e)
28607     {
28608         if(!this.pinching && !this.dragable){
28609             return;
28610         }
28611         
28612         var touches = e.browserEvent.touches;
28613         
28614         if(!touches){
28615             return;
28616         }
28617         
28618         if(this.dragable){
28619             this.onMouseMove(e);
28620             return;
28621         }
28622         
28623         var coords = [];
28624         
28625         for(var i = 0, finger; finger = touches[i]; i++){
28626             coords.push(finger.pageX, finger.pageY);
28627         }
28628         
28629         var x = Math.pow(coords[0] - coords[2], 2);
28630         var y = Math.pow(coords[1] - coords[3], 2);
28631         
28632         this.endDistance = Math.sqrt(x + y);
28633         
28634         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28635         
28636         if(!this.zoomable()){
28637             this.scale = this.startScale;
28638             return;
28639         }
28640         
28641         this.draw();
28642         
28643     },
28644     
28645     onTouchEnd : function(e)
28646     {
28647         this.pinching = false;
28648         this.dragable = false;
28649         
28650     },
28651     
28652     process : function(file, crop)
28653     {
28654         if(this.loadMask){
28655             this.maskEl.mask(this.loadingText);
28656         }
28657         
28658         this.xhr = new XMLHttpRequest();
28659         
28660         file.xhr = this.xhr;
28661
28662         this.xhr.open(this.method, this.url, true);
28663         
28664         var headers = {
28665             "Accept": "application/json",
28666             "Cache-Control": "no-cache",
28667             "X-Requested-With": "XMLHttpRequest"
28668         };
28669         
28670         for (var headerName in headers) {
28671             var headerValue = headers[headerName];
28672             if (headerValue) {
28673                 this.xhr.setRequestHeader(headerName, headerValue);
28674             }
28675         }
28676         
28677         var _this = this;
28678         
28679         this.xhr.onload = function()
28680         {
28681             _this.xhrOnLoad(_this.xhr);
28682         }
28683         
28684         this.xhr.onerror = function()
28685         {
28686             _this.xhrOnError(_this.xhr);
28687         }
28688         
28689         var formData = new FormData();
28690
28691         formData.append('returnHTML', 'NO');
28692         
28693         if(crop){
28694             formData.append('crop', crop);
28695         }
28696         
28697         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28698             formData.append(this.paramName, file, file.name);
28699         }
28700         
28701         if(typeof(file.filename) != 'undefined'){
28702             formData.append('filename', file.filename);
28703         }
28704         
28705         if(typeof(file.mimetype) != 'undefined'){
28706             formData.append('mimetype', file.mimetype);
28707         }
28708         
28709         if(this.fireEvent('arrange', this, formData) != false){
28710             this.xhr.send(formData);
28711         };
28712     },
28713     
28714     xhrOnLoad : function(xhr)
28715     {
28716         if(this.loadMask){
28717             this.maskEl.unmask();
28718         }
28719         
28720         if (xhr.readyState !== 4) {
28721             this.fireEvent('exception', this, xhr);
28722             return;
28723         }
28724
28725         var response = Roo.decode(xhr.responseText);
28726         
28727         if(!response.success){
28728             this.fireEvent('exception', this, xhr);
28729             return;
28730         }
28731         
28732         var response = Roo.decode(xhr.responseText);
28733         
28734         this.fireEvent('upload', this, response);
28735         
28736     },
28737     
28738     xhrOnError : function()
28739     {
28740         if(this.loadMask){
28741             this.maskEl.unmask();
28742         }
28743         
28744         Roo.log('xhr on error');
28745         
28746         var response = Roo.decode(xhr.responseText);
28747           
28748         Roo.log(response);
28749         
28750     },
28751     
28752     prepare : function(file)
28753     {   
28754         if(this.loadMask){
28755             this.maskEl.mask(this.loadingText);
28756         }
28757         
28758         this.file = false;
28759         this.exif = {};
28760         
28761         if(typeof(file) === 'string'){
28762             this.loadCanvas(file);
28763             return;
28764         }
28765         
28766         if(!file || !this.urlAPI){
28767             return;
28768         }
28769         
28770         this.file = file;
28771         this.cropType = file.type;
28772         
28773         var _this = this;
28774         
28775         if(this.fireEvent('prepare', this, this.file) != false){
28776             
28777             var reader = new FileReader();
28778             
28779             reader.onload = function (e) {
28780                 if (e.target.error) {
28781                     Roo.log(e.target.error);
28782                     return;
28783                 }
28784                 
28785                 var buffer = e.target.result,
28786                     dataView = new DataView(buffer),
28787                     offset = 2,
28788                     maxOffset = dataView.byteLength - 4,
28789                     markerBytes,
28790                     markerLength;
28791                 
28792                 if (dataView.getUint16(0) === 0xffd8) {
28793                     while (offset < maxOffset) {
28794                         markerBytes = dataView.getUint16(offset);
28795                         
28796                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28797                             markerLength = dataView.getUint16(offset + 2) + 2;
28798                             if (offset + markerLength > dataView.byteLength) {
28799                                 Roo.log('Invalid meta data: Invalid segment size.');
28800                                 break;
28801                             }
28802                             
28803                             if(markerBytes == 0xffe1){
28804                                 _this.parseExifData(
28805                                     dataView,
28806                                     offset,
28807                                     markerLength
28808                                 );
28809                             }
28810                             
28811                             offset += markerLength;
28812                             
28813                             continue;
28814                         }
28815                         
28816                         break;
28817                     }
28818                     
28819                 }
28820                 
28821                 var url = _this.urlAPI.createObjectURL(_this.file);
28822                 
28823                 _this.loadCanvas(url);
28824                 
28825                 return;
28826             }
28827             
28828             reader.readAsArrayBuffer(this.file);
28829             
28830         }
28831         
28832     },
28833     
28834     parseExifData : function(dataView, offset, length)
28835     {
28836         var tiffOffset = offset + 10,
28837             littleEndian,
28838             dirOffset;
28839     
28840         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28841             // No Exif data, might be XMP data instead
28842             return;
28843         }
28844         
28845         // Check for the ASCII code for "Exif" (0x45786966):
28846         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28847             // No Exif data, might be XMP data instead
28848             return;
28849         }
28850         if (tiffOffset + 8 > dataView.byteLength) {
28851             Roo.log('Invalid Exif data: Invalid segment size.');
28852             return;
28853         }
28854         // Check for the two null bytes:
28855         if (dataView.getUint16(offset + 8) !== 0x0000) {
28856             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28857             return;
28858         }
28859         // Check the byte alignment:
28860         switch (dataView.getUint16(tiffOffset)) {
28861         case 0x4949:
28862             littleEndian = true;
28863             break;
28864         case 0x4D4D:
28865             littleEndian = false;
28866             break;
28867         default:
28868             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28869             return;
28870         }
28871         // Check for the TIFF tag marker (0x002A):
28872         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28873             Roo.log('Invalid Exif data: Missing TIFF marker.');
28874             return;
28875         }
28876         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28877         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28878         
28879         this.parseExifTags(
28880             dataView,
28881             tiffOffset,
28882             tiffOffset + dirOffset,
28883             littleEndian
28884         );
28885     },
28886     
28887     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28888     {
28889         var tagsNumber,
28890             dirEndOffset,
28891             i;
28892         if (dirOffset + 6 > dataView.byteLength) {
28893             Roo.log('Invalid Exif data: Invalid directory offset.');
28894             return;
28895         }
28896         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28897         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28898         if (dirEndOffset + 4 > dataView.byteLength) {
28899             Roo.log('Invalid Exif data: Invalid directory size.');
28900             return;
28901         }
28902         for (i = 0; i < tagsNumber; i += 1) {
28903             this.parseExifTag(
28904                 dataView,
28905                 tiffOffset,
28906                 dirOffset + 2 + 12 * i, // tag offset
28907                 littleEndian
28908             );
28909         }
28910         // Return the offset to the next directory:
28911         return dataView.getUint32(dirEndOffset, littleEndian);
28912     },
28913     
28914     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28915     {
28916         var tag = dataView.getUint16(offset, littleEndian);
28917         
28918         this.exif[tag] = this.getExifValue(
28919             dataView,
28920             tiffOffset,
28921             offset,
28922             dataView.getUint16(offset + 2, littleEndian), // tag type
28923             dataView.getUint32(offset + 4, littleEndian), // tag length
28924             littleEndian
28925         );
28926     },
28927     
28928     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28929     {
28930         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28931             tagSize,
28932             dataOffset,
28933             values,
28934             i,
28935             str,
28936             c;
28937     
28938         if (!tagType) {
28939             Roo.log('Invalid Exif data: Invalid tag type.');
28940             return;
28941         }
28942         
28943         tagSize = tagType.size * length;
28944         // Determine if the value is contained in the dataOffset bytes,
28945         // or if the value at the dataOffset is a pointer to the actual data:
28946         dataOffset = tagSize > 4 ?
28947                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28948         if (dataOffset + tagSize > dataView.byteLength) {
28949             Roo.log('Invalid Exif data: Invalid data offset.');
28950             return;
28951         }
28952         if (length === 1) {
28953             return tagType.getValue(dataView, dataOffset, littleEndian);
28954         }
28955         values = [];
28956         for (i = 0; i < length; i += 1) {
28957             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28958         }
28959         
28960         if (tagType.ascii) {
28961             str = '';
28962             // Concatenate the chars:
28963             for (i = 0; i < values.length; i += 1) {
28964                 c = values[i];
28965                 // Ignore the terminating NULL byte(s):
28966                 if (c === '\u0000') {
28967                     break;
28968                 }
28969                 str += c;
28970             }
28971             return str;
28972         }
28973         return values;
28974     }
28975     
28976 });
28977
28978 Roo.apply(Roo.bootstrap.UploadCropbox, {
28979     tags : {
28980         'Orientation': 0x0112
28981     },
28982     
28983     Orientation: {
28984             1: 0, //'top-left',
28985 //            2: 'top-right',
28986             3: 180, //'bottom-right',
28987 //            4: 'bottom-left',
28988 //            5: 'left-top',
28989             6: 90, //'right-top',
28990 //            7: 'right-bottom',
28991             8: 270 //'left-bottom'
28992     },
28993     
28994     exifTagTypes : {
28995         // byte, 8-bit unsigned int:
28996         1: {
28997             getValue: function (dataView, dataOffset) {
28998                 return dataView.getUint8(dataOffset);
28999             },
29000             size: 1
29001         },
29002         // ascii, 8-bit byte:
29003         2: {
29004             getValue: function (dataView, dataOffset) {
29005                 return String.fromCharCode(dataView.getUint8(dataOffset));
29006             },
29007             size: 1,
29008             ascii: true
29009         },
29010         // short, 16 bit int:
29011         3: {
29012             getValue: function (dataView, dataOffset, littleEndian) {
29013                 return dataView.getUint16(dataOffset, littleEndian);
29014             },
29015             size: 2
29016         },
29017         // long, 32 bit int:
29018         4: {
29019             getValue: function (dataView, dataOffset, littleEndian) {
29020                 return dataView.getUint32(dataOffset, littleEndian);
29021             },
29022             size: 4
29023         },
29024         // rational = two long values, first is numerator, second is denominator:
29025         5: {
29026             getValue: function (dataView, dataOffset, littleEndian) {
29027                 return dataView.getUint32(dataOffset, littleEndian) /
29028                     dataView.getUint32(dataOffset + 4, littleEndian);
29029             },
29030             size: 8
29031         },
29032         // slong, 32 bit signed int:
29033         9: {
29034             getValue: function (dataView, dataOffset, littleEndian) {
29035                 return dataView.getInt32(dataOffset, littleEndian);
29036             },
29037             size: 4
29038         },
29039         // srational, two slongs, first is numerator, second is denominator:
29040         10: {
29041             getValue: function (dataView, dataOffset, littleEndian) {
29042                 return dataView.getInt32(dataOffset, littleEndian) /
29043                     dataView.getInt32(dataOffset + 4, littleEndian);
29044             },
29045             size: 8
29046         }
29047     },
29048     
29049     footer : {
29050         STANDARD : [
29051             {
29052                 tag : 'div',
29053                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29054                 action : 'rotate-left',
29055                 cn : [
29056                     {
29057                         tag : 'button',
29058                         cls : 'btn btn-default',
29059                         html : '<i class="fa fa-undo"></i>'
29060                     }
29061                 ]
29062             },
29063             {
29064                 tag : 'div',
29065                 cls : 'btn-group roo-upload-cropbox-picture',
29066                 action : 'picture',
29067                 cn : [
29068                     {
29069                         tag : 'button',
29070                         cls : 'btn btn-default',
29071                         html : '<i class="fa fa-picture-o"></i>'
29072                     }
29073                 ]
29074             },
29075             {
29076                 tag : 'div',
29077                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29078                 action : 'rotate-right',
29079                 cn : [
29080                     {
29081                         tag : 'button',
29082                         cls : 'btn btn-default',
29083                         html : '<i class="fa fa-repeat"></i>'
29084                     }
29085                 ]
29086             }
29087         ],
29088         DOCUMENT : [
29089             {
29090                 tag : 'div',
29091                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29092                 action : 'rotate-left',
29093                 cn : [
29094                     {
29095                         tag : 'button',
29096                         cls : 'btn btn-default',
29097                         html : '<i class="fa fa-undo"></i>'
29098                     }
29099                 ]
29100             },
29101             {
29102                 tag : 'div',
29103                 cls : 'btn-group roo-upload-cropbox-download',
29104                 action : 'download',
29105                 cn : [
29106                     {
29107                         tag : 'button',
29108                         cls : 'btn btn-default',
29109                         html : '<i class="fa fa-download"></i>'
29110                     }
29111                 ]
29112             },
29113             {
29114                 tag : 'div',
29115                 cls : 'btn-group roo-upload-cropbox-crop',
29116                 action : 'crop',
29117                 cn : [
29118                     {
29119                         tag : 'button',
29120                         cls : 'btn btn-default',
29121                         html : '<i class="fa fa-crop"></i>'
29122                     }
29123                 ]
29124             },
29125             {
29126                 tag : 'div',
29127                 cls : 'btn-group roo-upload-cropbox-trash',
29128                 action : 'trash',
29129                 cn : [
29130                     {
29131                         tag : 'button',
29132                         cls : 'btn btn-default',
29133                         html : '<i class="fa fa-trash"></i>'
29134                     }
29135                 ]
29136             },
29137             {
29138                 tag : 'div',
29139                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29140                 action : 'rotate-right',
29141                 cn : [
29142                     {
29143                         tag : 'button',
29144                         cls : 'btn btn-default',
29145                         html : '<i class="fa fa-repeat"></i>'
29146                     }
29147                 ]
29148             }
29149         ],
29150         ROTATOR : [
29151             {
29152                 tag : 'div',
29153                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29154                 action : 'rotate-left',
29155                 cn : [
29156                     {
29157                         tag : 'button',
29158                         cls : 'btn btn-default',
29159                         html : '<i class="fa fa-undo"></i>'
29160                     }
29161                 ]
29162             },
29163             {
29164                 tag : 'div',
29165                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29166                 action : 'rotate-right',
29167                 cn : [
29168                     {
29169                         tag : 'button',
29170                         cls : 'btn btn-default',
29171                         html : '<i class="fa fa-repeat"></i>'
29172                     }
29173                 ]
29174             }
29175         ]
29176     }
29177 });
29178
29179 /*
29180 * Licence: LGPL
29181 */
29182
29183 /**
29184  * @class Roo.bootstrap.DocumentManager
29185  * @extends Roo.bootstrap.Component
29186  * Bootstrap DocumentManager class
29187  * @cfg {String} paramName default 'imageUpload'
29188  * @cfg {String} toolTipName default 'filename'
29189  * @cfg {String} method default POST
29190  * @cfg {String} url action url
29191  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29192  * @cfg {Boolean} multiple multiple upload default true
29193  * @cfg {Number} thumbSize default 300
29194  * @cfg {String} fieldLabel
29195  * @cfg {Number} labelWidth default 4
29196  * @cfg {String} labelAlign (left|top) default left
29197  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29198 * @cfg {Number} labellg set the width of label (1-12)
29199  * @cfg {Number} labelmd set the width of label (1-12)
29200  * @cfg {Number} labelsm set the width of label (1-12)
29201  * @cfg {Number} labelxs set the width of label (1-12)
29202  * 
29203  * @constructor
29204  * Create a new DocumentManager
29205  * @param {Object} config The config object
29206  */
29207
29208 Roo.bootstrap.DocumentManager = function(config){
29209     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29210     
29211     this.files = [];
29212     this.delegates = [];
29213     
29214     this.addEvents({
29215         /**
29216          * @event initial
29217          * Fire when initial the DocumentManager
29218          * @param {Roo.bootstrap.DocumentManager} this
29219          */
29220         "initial" : true,
29221         /**
29222          * @event inspect
29223          * inspect selected file
29224          * @param {Roo.bootstrap.DocumentManager} this
29225          * @param {File} file
29226          */
29227         "inspect" : true,
29228         /**
29229          * @event exception
29230          * Fire when xhr load exception
29231          * @param {Roo.bootstrap.DocumentManager} this
29232          * @param {XMLHttpRequest} xhr
29233          */
29234         "exception" : true,
29235         /**
29236          * @event afterupload
29237          * Fire when xhr load exception
29238          * @param {Roo.bootstrap.DocumentManager} this
29239          * @param {XMLHttpRequest} xhr
29240          */
29241         "afterupload" : true,
29242         /**
29243          * @event prepare
29244          * prepare the form data
29245          * @param {Roo.bootstrap.DocumentManager} this
29246          * @param {Object} formData
29247          */
29248         "prepare" : true,
29249         /**
29250          * @event remove
29251          * Fire when remove the file
29252          * @param {Roo.bootstrap.DocumentManager} this
29253          * @param {Object} file
29254          */
29255         "remove" : true,
29256         /**
29257          * @event refresh
29258          * Fire after refresh the file
29259          * @param {Roo.bootstrap.DocumentManager} this
29260          */
29261         "refresh" : true,
29262         /**
29263          * @event click
29264          * Fire after click the image
29265          * @param {Roo.bootstrap.DocumentManager} this
29266          * @param {Object} file
29267          */
29268         "click" : true,
29269         /**
29270          * @event edit
29271          * Fire when upload a image and editable set to true
29272          * @param {Roo.bootstrap.DocumentManager} this
29273          * @param {Object} file
29274          */
29275         "edit" : true,
29276         /**
29277          * @event beforeselectfile
29278          * Fire before select file
29279          * @param {Roo.bootstrap.DocumentManager} this
29280          */
29281         "beforeselectfile" : true,
29282         /**
29283          * @event process
29284          * Fire before process file
29285          * @param {Roo.bootstrap.DocumentManager} this
29286          * @param {Object} file
29287          */
29288         "process" : true,
29289         /**
29290          * @event previewrendered
29291          * Fire when preview rendered
29292          * @param {Roo.bootstrap.DocumentManager} this
29293          * @param {Object} file
29294          */
29295         "previewrendered" : true,
29296         /**
29297          */
29298         "previewResize" : true
29299         
29300     });
29301 };
29302
29303 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29304     
29305     boxes : 0,
29306     inputName : '',
29307     thumbSize : 300,
29308     multiple : true,
29309     files : false,
29310     method : 'POST',
29311     url : '',
29312     paramName : 'imageUpload',
29313     toolTipName : 'filename',
29314     fieldLabel : '',
29315     labelWidth : 4,
29316     labelAlign : 'left',
29317     editable : true,
29318     delegates : false,
29319     xhr : false, 
29320     
29321     labellg : 0,
29322     labelmd : 0,
29323     labelsm : 0,
29324     labelxs : 0,
29325     
29326     getAutoCreate : function()
29327     {   
29328         var managerWidget = {
29329             tag : 'div',
29330             cls : 'roo-document-manager',
29331             cn : [
29332                 {
29333                     tag : 'input',
29334                     cls : 'roo-document-manager-selector',
29335                     type : 'file'
29336                 },
29337                 {
29338                     tag : 'div',
29339                     cls : 'roo-document-manager-uploader',
29340                     cn : [
29341                         {
29342                             tag : 'div',
29343                             cls : 'roo-document-manager-upload-btn',
29344                             html : '<i class="fa fa-plus"></i>'
29345                         }
29346                     ]
29347                     
29348                 }
29349             ]
29350         };
29351         
29352         var content = [
29353             {
29354                 tag : 'div',
29355                 cls : 'column col-md-12',
29356                 cn : managerWidget
29357             }
29358         ];
29359         
29360         if(this.fieldLabel.length){
29361             
29362             content = [
29363                 {
29364                     tag : 'div',
29365                     cls : 'column col-md-12',
29366                     html : this.fieldLabel
29367                 },
29368                 {
29369                     tag : 'div',
29370                     cls : 'column col-md-12',
29371                     cn : managerWidget
29372                 }
29373             ];
29374
29375             if(this.labelAlign == 'left'){
29376                 content = [
29377                     {
29378                         tag : 'div',
29379                         cls : 'column',
29380                         html : this.fieldLabel
29381                     },
29382                     {
29383                         tag : 'div',
29384                         cls : 'column',
29385                         cn : managerWidget
29386                     }
29387                 ];
29388                 
29389                 if(this.labelWidth > 12){
29390                     content[0].style = "width: " + this.labelWidth + 'px';
29391                 }
29392
29393                 if(this.labelWidth < 13 && this.labelmd == 0){
29394                     this.labelmd = this.labelWidth;
29395                 }
29396
29397                 if(this.labellg > 0){
29398                     content[0].cls += ' col-lg-' + this.labellg;
29399                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29400                 }
29401
29402                 if(this.labelmd > 0){
29403                     content[0].cls += ' col-md-' + this.labelmd;
29404                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29405                 }
29406
29407                 if(this.labelsm > 0){
29408                     content[0].cls += ' col-sm-' + this.labelsm;
29409                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29410                 }
29411
29412                 if(this.labelxs > 0){
29413                     content[0].cls += ' col-xs-' + this.labelxs;
29414                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29415                 }
29416                 
29417             }
29418         }
29419         
29420         var cfg = {
29421             tag : 'div',
29422             cls : 'row clearfix',
29423             cn : content
29424         };
29425         
29426         return cfg;
29427         
29428     },
29429     
29430     initEvents : function()
29431     {
29432         this.managerEl = this.el.select('.roo-document-manager', true).first();
29433         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29434         
29435         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29436         this.selectorEl.hide();
29437         
29438         if(this.multiple){
29439             this.selectorEl.attr('multiple', 'multiple');
29440         }
29441         
29442         this.selectorEl.on('change', this.onFileSelected, this);
29443         
29444         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29445         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29446         
29447         this.uploader.on('click', this.onUploaderClick, this);
29448         
29449         this.renderProgressDialog();
29450         
29451         var _this = this;
29452         
29453         window.addEventListener("resize", function() { _this.refresh(); } );
29454         
29455         this.fireEvent('initial', this);
29456     },
29457     
29458     renderProgressDialog : function()
29459     {
29460         var _this = this;
29461         
29462         this.progressDialog = new Roo.bootstrap.Modal({
29463             cls : 'roo-document-manager-progress-dialog',
29464             allow_close : false,
29465             animate : false,
29466             title : '',
29467             buttons : [
29468                 {
29469                     name  :'cancel',
29470                     weight : 'danger',
29471                     html : 'Cancel'
29472                 }
29473             ], 
29474             listeners : { 
29475                 btnclick : function() {
29476                     _this.uploadCancel();
29477                     this.hide();
29478                 }
29479             }
29480         });
29481          
29482         this.progressDialog.render(Roo.get(document.body));
29483          
29484         this.progress = new Roo.bootstrap.Progress({
29485             cls : 'roo-document-manager-progress',
29486             active : true,
29487             striped : true
29488         });
29489         
29490         this.progress.render(this.progressDialog.getChildContainer());
29491         
29492         this.progressBar = new Roo.bootstrap.ProgressBar({
29493             cls : 'roo-document-manager-progress-bar',
29494             aria_valuenow : 0,
29495             aria_valuemin : 0,
29496             aria_valuemax : 12,
29497             panel : 'success'
29498         });
29499         
29500         this.progressBar.render(this.progress.getChildContainer());
29501     },
29502     
29503     onUploaderClick : function(e)
29504     {
29505         e.preventDefault();
29506      
29507         if(this.fireEvent('beforeselectfile', this) != false){
29508             this.selectorEl.dom.click();
29509         }
29510         
29511     },
29512     
29513     onFileSelected : function(e)
29514     {
29515         e.preventDefault();
29516         
29517         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29518             return;
29519         }
29520         
29521         Roo.each(this.selectorEl.dom.files, function(file){
29522             if(this.fireEvent('inspect', this, file) != false){
29523                 this.files.push(file);
29524             }
29525         }, this);
29526         
29527         this.queue();
29528         
29529     },
29530     
29531     queue : function()
29532     {
29533         this.selectorEl.dom.value = '';
29534         
29535         if(!this.files || !this.files.length){
29536             return;
29537         }
29538         
29539         if(this.boxes > 0 && this.files.length > this.boxes){
29540             this.files = this.files.slice(0, this.boxes);
29541         }
29542         
29543         this.uploader.show();
29544         
29545         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29546             this.uploader.hide();
29547         }
29548         
29549         var _this = this;
29550         
29551         var files = [];
29552         
29553         var docs = [];
29554         
29555         Roo.each(this.files, function(file){
29556             
29557             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29558                 var f = this.renderPreview(file);
29559                 files.push(f);
29560                 return;
29561             }
29562             
29563             if(file.type.indexOf('image') != -1){
29564                 this.delegates.push(
29565                     (function(){
29566                         _this.process(file);
29567                     }).createDelegate(this)
29568                 );
29569         
29570                 return;
29571             }
29572             
29573             docs.push(
29574                 (function(){
29575                     _this.process(file);
29576                 }).createDelegate(this)
29577             );
29578             
29579         }, this);
29580         
29581         this.files = files;
29582         
29583         this.delegates = this.delegates.concat(docs);
29584         
29585         if(!this.delegates.length){
29586             this.refresh();
29587             return;
29588         }
29589         
29590         this.progressBar.aria_valuemax = this.delegates.length;
29591         
29592         this.arrange();
29593         
29594         return;
29595     },
29596     
29597     arrange : function()
29598     {
29599         if(!this.delegates.length){
29600             this.progressDialog.hide();
29601             this.refresh();
29602             return;
29603         }
29604         
29605         var delegate = this.delegates.shift();
29606         
29607         this.progressDialog.show();
29608         
29609         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29610         
29611         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29612         
29613         delegate();
29614     },
29615     
29616     refresh : function()
29617     {
29618         this.uploader.show();
29619         
29620         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29621             this.uploader.hide();
29622         }
29623         
29624         Roo.isTouch ? this.closable(false) : this.closable(true);
29625         
29626         this.fireEvent('refresh', this);
29627     },
29628     
29629     onRemove : function(e, el, o)
29630     {
29631         e.preventDefault();
29632         
29633         this.fireEvent('remove', this, o);
29634         
29635     },
29636     
29637     remove : function(o)
29638     {
29639         var files = [];
29640         
29641         Roo.each(this.files, function(file){
29642             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29643                 files.push(file);
29644                 return;
29645             }
29646
29647             o.target.remove();
29648
29649         }, this);
29650         
29651         this.files = files;
29652         
29653         this.refresh();
29654     },
29655     
29656     clear : function()
29657     {
29658         Roo.each(this.files, function(file){
29659             if(!file.target){
29660                 return;
29661             }
29662             
29663             file.target.remove();
29664
29665         }, this);
29666         
29667         this.files = [];
29668         
29669         this.refresh();
29670     },
29671     
29672     onClick : function(e, el, o)
29673     {
29674         e.preventDefault();
29675         
29676         this.fireEvent('click', this, o);
29677         
29678     },
29679     
29680     closable : function(closable)
29681     {
29682         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29683             
29684             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29685             
29686             if(closable){
29687                 el.show();
29688                 return;
29689             }
29690             
29691             el.hide();
29692             
29693         }, this);
29694     },
29695     
29696     xhrOnLoad : function(xhr)
29697     {
29698         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29699             el.remove();
29700         }, this);
29701         
29702         if (xhr.readyState !== 4) {
29703             this.arrange();
29704             this.fireEvent('exception', this, xhr);
29705             return;
29706         }
29707
29708         var response = Roo.decode(xhr.responseText);
29709         
29710         if(!response.success){
29711             this.arrange();
29712             this.fireEvent('exception', this, xhr);
29713             return;
29714         }
29715         
29716         var file = this.renderPreview(response.data);
29717         
29718         this.files.push(file);
29719         
29720         this.arrange();
29721         
29722         this.fireEvent('afterupload', this, xhr);
29723         
29724     },
29725     
29726     xhrOnError : function(xhr)
29727     {
29728         Roo.log('xhr on error');
29729         
29730         var response = Roo.decode(xhr.responseText);
29731           
29732         Roo.log(response);
29733         
29734         this.arrange();
29735     },
29736     
29737     process : function(file)
29738     {
29739         if(this.fireEvent('process', this, file) !== false){
29740             if(this.editable && file.type.indexOf('image') != -1){
29741                 this.fireEvent('edit', this, file);
29742                 return;
29743             }
29744
29745             this.uploadStart(file, false);
29746
29747             return;
29748         }
29749         
29750     },
29751     
29752     uploadStart : function(file, crop)
29753     {
29754         this.xhr = new XMLHttpRequest();
29755         
29756         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29757             this.arrange();
29758             return;
29759         }
29760         
29761         file.xhr = this.xhr;
29762             
29763         this.managerEl.createChild({
29764             tag : 'div',
29765             cls : 'roo-document-manager-loading',
29766             cn : [
29767                 {
29768                     tag : 'div',
29769                     tooltip : file.name,
29770                     cls : 'roo-document-manager-thumb',
29771                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29772                 }
29773             ]
29774
29775         });
29776
29777         this.xhr.open(this.method, this.url, true);
29778         
29779         var headers = {
29780             "Accept": "application/json",
29781             "Cache-Control": "no-cache",
29782             "X-Requested-With": "XMLHttpRequest"
29783         };
29784         
29785         for (var headerName in headers) {
29786             var headerValue = headers[headerName];
29787             if (headerValue) {
29788                 this.xhr.setRequestHeader(headerName, headerValue);
29789             }
29790         }
29791         
29792         var _this = this;
29793         
29794         this.xhr.onload = function()
29795         {
29796             _this.xhrOnLoad(_this.xhr);
29797         }
29798         
29799         this.xhr.onerror = function()
29800         {
29801             _this.xhrOnError(_this.xhr);
29802         }
29803         
29804         var formData = new FormData();
29805
29806         formData.append('returnHTML', 'NO');
29807         
29808         if(crop){
29809             formData.append('crop', crop);
29810         }
29811         
29812         formData.append(this.paramName, file, file.name);
29813         
29814         var options = {
29815             file : file, 
29816             manually : false
29817         };
29818         
29819         if(this.fireEvent('prepare', this, formData, options) != false){
29820             
29821             if(options.manually){
29822                 return;
29823             }
29824             
29825             this.xhr.send(formData);
29826             return;
29827         };
29828         
29829         this.uploadCancel();
29830     },
29831     
29832     uploadCancel : function()
29833     {
29834         if (this.xhr) {
29835             this.xhr.abort();
29836         }
29837         
29838         this.delegates = [];
29839         
29840         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29841             el.remove();
29842         }, this);
29843         
29844         this.arrange();
29845     },
29846     
29847     renderPreview : function(file)
29848     {
29849         if(typeof(file.target) != 'undefined' && file.target){
29850             return file;
29851         }
29852         
29853         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29854         
29855         var previewEl = this.managerEl.createChild({
29856             tag : 'div',
29857             cls : 'roo-document-manager-preview',
29858             cn : [
29859                 {
29860                     tag : 'div',
29861                     tooltip : file[this.toolTipName],
29862                     cls : 'roo-document-manager-thumb',
29863                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29864                 },
29865                 {
29866                     tag : 'button',
29867                     cls : 'close',
29868                     html : '<i class="fa fa-times-circle"></i>'
29869                 }
29870             ]
29871         });
29872
29873         var close = previewEl.select('button.close', true).first();
29874
29875         close.on('click', this.onRemove, this, file);
29876
29877         file.target = previewEl;
29878
29879         var image = previewEl.select('img', true).first();
29880         
29881         var _this = this;
29882         
29883         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29884         
29885         image.on('click', this.onClick, this, file);
29886         
29887         this.fireEvent('previewrendered', this, file);
29888         
29889         return file;
29890         
29891     },
29892     
29893     onPreviewLoad : function(file, image)
29894     {
29895         if(typeof(file.target) == 'undefined' || !file.target){
29896             return;
29897         }
29898         
29899         var width = image.dom.naturalWidth || image.dom.width;
29900         var height = image.dom.naturalHeight || image.dom.height;
29901         
29902         if(!this.previewResize) {
29903             return;
29904         }
29905         
29906         if(width > height){
29907             file.target.addClass('wide');
29908             return;
29909         }
29910         
29911         file.target.addClass('tall');
29912         return;
29913         
29914     },
29915     
29916     uploadFromSource : function(file, crop)
29917     {
29918         this.xhr = new XMLHttpRequest();
29919         
29920         this.managerEl.createChild({
29921             tag : 'div',
29922             cls : 'roo-document-manager-loading',
29923             cn : [
29924                 {
29925                     tag : 'div',
29926                     tooltip : file.name,
29927                     cls : 'roo-document-manager-thumb',
29928                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29929                 }
29930             ]
29931
29932         });
29933
29934         this.xhr.open(this.method, this.url, true);
29935         
29936         var headers = {
29937             "Accept": "application/json",
29938             "Cache-Control": "no-cache",
29939             "X-Requested-With": "XMLHttpRequest"
29940         };
29941         
29942         for (var headerName in headers) {
29943             var headerValue = headers[headerName];
29944             if (headerValue) {
29945                 this.xhr.setRequestHeader(headerName, headerValue);
29946             }
29947         }
29948         
29949         var _this = this;
29950         
29951         this.xhr.onload = function()
29952         {
29953             _this.xhrOnLoad(_this.xhr);
29954         }
29955         
29956         this.xhr.onerror = function()
29957         {
29958             _this.xhrOnError(_this.xhr);
29959         }
29960         
29961         var formData = new FormData();
29962
29963         formData.append('returnHTML', 'NO');
29964         
29965         formData.append('crop', crop);
29966         
29967         if(typeof(file.filename) != 'undefined'){
29968             formData.append('filename', file.filename);
29969         }
29970         
29971         if(typeof(file.mimetype) != 'undefined'){
29972             formData.append('mimetype', file.mimetype);
29973         }
29974         
29975         Roo.log(formData);
29976         
29977         if(this.fireEvent('prepare', this, formData) != false){
29978             this.xhr.send(formData);
29979         };
29980     }
29981 });
29982
29983 /*
29984 * Licence: LGPL
29985 */
29986
29987 /**
29988  * @class Roo.bootstrap.DocumentViewer
29989  * @extends Roo.bootstrap.Component
29990  * Bootstrap DocumentViewer class
29991  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29992  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29993  * 
29994  * @constructor
29995  * Create a new DocumentViewer
29996  * @param {Object} config The config object
29997  */
29998
29999 Roo.bootstrap.DocumentViewer = function(config){
30000     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
30001     
30002     this.addEvents({
30003         /**
30004          * @event initial
30005          * Fire after initEvent
30006          * @param {Roo.bootstrap.DocumentViewer} this
30007          */
30008         "initial" : true,
30009         /**
30010          * @event click
30011          * Fire after click
30012          * @param {Roo.bootstrap.DocumentViewer} this
30013          */
30014         "click" : true,
30015         /**
30016          * @event download
30017          * Fire after download button
30018          * @param {Roo.bootstrap.DocumentViewer} this
30019          */
30020         "download" : true,
30021         /**
30022          * @event trash
30023          * Fire after trash button
30024          * @param {Roo.bootstrap.DocumentViewer} this
30025          */
30026         "trash" : true
30027         
30028     });
30029 };
30030
30031 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
30032     
30033     showDownload : true,
30034     
30035     showTrash : true,
30036     
30037     getAutoCreate : function()
30038     {
30039         var cfg = {
30040             tag : 'div',
30041             cls : 'roo-document-viewer',
30042             cn : [
30043                 {
30044                     tag : 'div',
30045                     cls : 'roo-document-viewer-body',
30046                     cn : [
30047                         {
30048                             tag : 'div',
30049                             cls : 'roo-document-viewer-thumb',
30050                             cn : [
30051                                 {
30052                                     tag : 'img',
30053                                     cls : 'roo-document-viewer-image'
30054                                 }
30055                             ]
30056                         }
30057                     ]
30058                 },
30059                 {
30060                     tag : 'div',
30061                     cls : 'roo-document-viewer-footer',
30062                     cn : {
30063                         tag : 'div',
30064                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
30065                         cn : [
30066                             {
30067                                 tag : 'div',
30068                                 cls : 'btn-group roo-document-viewer-download',
30069                                 cn : [
30070                                     {
30071                                         tag : 'button',
30072                                         cls : 'btn btn-default',
30073                                         html : '<i class="fa fa-download"></i>'
30074                                     }
30075                                 ]
30076                             },
30077                             {
30078                                 tag : 'div',
30079                                 cls : 'btn-group roo-document-viewer-trash',
30080                                 cn : [
30081                                     {
30082                                         tag : 'button',
30083                                         cls : 'btn btn-default',
30084                                         html : '<i class="fa fa-trash"></i>'
30085                                     }
30086                                 ]
30087                             }
30088                         ]
30089                     }
30090                 }
30091             ]
30092         };
30093         
30094         return cfg;
30095     },
30096     
30097     initEvents : function()
30098     {
30099         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30100         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30101         
30102         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30103         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30104         
30105         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30106         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30107         
30108         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30109         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30110         
30111         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30112         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30113         
30114         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30115         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30116         
30117         this.bodyEl.on('click', this.onClick, this);
30118         this.downloadBtn.on('click', this.onDownload, this);
30119         this.trashBtn.on('click', this.onTrash, this);
30120         
30121         this.downloadBtn.hide();
30122         this.trashBtn.hide();
30123         
30124         if(this.showDownload){
30125             this.downloadBtn.show();
30126         }
30127         
30128         if(this.showTrash){
30129             this.trashBtn.show();
30130         }
30131         
30132         if(!this.showDownload && !this.showTrash) {
30133             this.footerEl.hide();
30134         }
30135         
30136     },
30137     
30138     initial : function()
30139     {
30140         this.fireEvent('initial', this);
30141         
30142     },
30143     
30144     onClick : function(e)
30145     {
30146         e.preventDefault();
30147         
30148         this.fireEvent('click', this);
30149     },
30150     
30151     onDownload : function(e)
30152     {
30153         e.preventDefault();
30154         
30155         this.fireEvent('download', this);
30156     },
30157     
30158     onTrash : function(e)
30159     {
30160         e.preventDefault();
30161         
30162         this.fireEvent('trash', this);
30163     }
30164     
30165 });
30166 /*
30167  * - LGPL
30168  *
30169  * nav progress bar
30170  * 
30171  */
30172
30173 /**
30174  * @class Roo.bootstrap.NavProgressBar
30175  * @extends Roo.bootstrap.Component
30176  * Bootstrap NavProgressBar class
30177  * 
30178  * @constructor
30179  * Create a new nav progress bar
30180  * @param {Object} config The config object
30181  */
30182
30183 Roo.bootstrap.NavProgressBar = function(config){
30184     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30185
30186     this.bullets = this.bullets || [];
30187    
30188 //    Roo.bootstrap.NavProgressBar.register(this);
30189      this.addEvents({
30190         /**
30191              * @event changed
30192              * Fires when the active item changes
30193              * @param {Roo.bootstrap.NavProgressBar} this
30194              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30195              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30196          */
30197         'changed': true
30198      });
30199     
30200 };
30201
30202 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30203     
30204     bullets : [],
30205     barItems : [],
30206     
30207     getAutoCreate : function()
30208     {
30209         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30210         
30211         cfg = {
30212             tag : 'div',
30213             cls : 'roo-navigation-bar-group',
30214             cn : [
30215                 {
30216                     tag : 'div',
30217                     cls : 'roo-navigation-top-bar'
30218                 },
30219                 {
30220                     tag : 'div',
30221                     cls : 'roo-navigation-bullets-bar',
30222                     cn : [
30223                         {
30224                             tag : 'ul',
30225                             cls : 'roo-navigation-bar'
30226                         }
30227                     ]
30228                 },
30229                 
30230                 {
30231                     tag : 'div',
30232                     cls : 'roo-navigation-bottom-bar'
30233                 }
30234             ]
30235             
30236         };
30237         
30238         return cfg;
30239         
30240     },
30241     
30242     initEvents: function() 
30243     {
30244         
30245     },
30246     
30247     onRender : function(ct, position) 
30248     {
30249         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30250         
30251         if(this.bullets.length){
30252             Roo.each(this.bullets, function(b){
30253                this.addItem(b);
30254             }, this);
30255         }
30256         
30257         this.format();
30258         
30259     },
30260     
30261     addItem : function(cfg)
30262     {
30263         var item = new Roo.bootstrap.NavProgressItem(cfg);
30264         
30265         item.parentId = this.id;
30266         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30267         
30268         if(cfg.html){
30269             var top = new Roo.bootstrap.Element({
30270                 tag : 'div',
30271                 cls : 'roo-navigation-bar-text'
30272             });
30273             
30274             var bottom = new Roo.bootstrap.Element({
30275                 tag : 'div',
30276                 cls : 'roo-navigation-bar-text'
30277             });
30278             
30279             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30280             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30281             
30282             var topText = new Roo.bootstrap.Element({
30283                 tag : 'span',
30284                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30285             });
30286             
30287             var bottomText = new Roo.bootstrap.Element({
30288                 tag : 'span',
30289                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30290             });
30291             
30292             topText.onRender(top.el, null);
30293             bottomText.onRender(bottom.el, null);
30294             
30295             item.topEl = top;
30296             item.bottomEl = bottom;
30297         }
30298         
30299         this.barItems.push(item);
30300         
30301         return item;
30302     },
30303     
30304     getActive : function()
30305     {
30306         var active = false;
30307         
30308         Roo.each(this.barItems, function(v){
30309             
30310             if (!v.isActive()) {
30311                 return;
30312             }
30313             
30314             active = v;
30315             return false;
30316             
30317         });
30318         
30319         return active;
30320     },
30321     
30322     setActiveItem : function(item)
30323     {
30324         var prev = false;
30325         
30326         Roo.each(this.barItems, function(v){
30327             if (v.rid == item.rid) {
30328                 return ;
30329             }
30330             
30331             if (v.isActive()) {
30332                 v.setActive(false);
30333                 prev = v;
30334             }
30335         });
30336
30337         item.setActive(true);
30338         
30339         this.fireEvent('changed', this, item, prev);
30340     },
30341     
30342     getBarItem: function(rid)
30343     {
30344         var ret = false;
30345         
30346         Roo.each(this.barItems, function(e) {
30347             if (e.rid != rid) {
30348                 return;
30349             }
30350             
30351             ret =  e;
30352             return false;
30353         });
30354         
30355         return ret;
30356     },
30357     
30358     indexOfItem : function(item)
30359     {
30360         var index = false;
30361         
30362         Roo.each(this.barItems, function(v, i){
30363             
30364             if (v.rid != item.rid) {
30365                 return;
30366             }
30367             
30368             index = i;
30369             return false
30370         });
30371         
30372         return index;
30373     },
30374     
30375     setActiveNext : function()
30376     {
30377         var i = this.indexOfItem(this.getActive());
30378         
30379         if (i > this.barItems.length) {
30380             return;
30381         }
30382         
30383         this.setActiveItem(this.barItems[i+1]);
30384     },
30385     
30386     setActivePrev : function()
30387     {
30388         var i = this.indexOfItem(this.getActive());
30389         
30390         if (i  < 1) {
30391             return;
30392         }
30393         
30394         this.setActiveItem(this.barItems[i-1]);
30395     },
30396     
30397     format : function()
30398     {
30399         if(!this.barItems.length){
30400             return;
30401         }
30402      
30403         var width = 100 / this.barItems.length;
30404         
30405         Roo.each(this.barItems, function(i){
30406             i.el.setStyle('width', width + '%');
30407             i.topEl.el.setStyle('width', width + '%');
30408             i.bottomEl.el.setStyle('width', width + '%');
30409         }, this);
30410         
30411     }
30412     
30413 });
30414 /*
30415  * - LGPL
30416  *
30417  * Nav Progress Item
30418  * 
30419  */
30420
30421 /**
30422  * @class Roo.bootstrap.NavProgressItem
30423  * @extends Roo.bootstrap.Component
30424  * Bootstrap NavProgressItem class
30425  * @cfg {String} rid the reference id
30426  * @cfg {Boolean} active (true|false) Is item active default false
30427  * @cfg {Boolean} disabled (true|false) Is item active default false
30428  * @cfg {String} html
30429  * @cfg {String} position (top|bottom) text position default bottom
30430  * @cfg {String} icon show icon instead of number
30431  * 
30432  * @constructor
30433  * Create a new NavProgressItem
30434  * @param {Object} config The config object
30435  */
30436 Roo.bootstrap.NavProgressItem = function(config){
30437     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30438     this.addEvents({
30439         // raw events
30440         /**
30441          * @event click
30442          * The raw click event for the entire grid.
30443          * @param {Roo.bootstrap.NavProgressItem} this
30444          * @param {Roo.EventObject} e
30445          */
30446         "click" : true
30447     });
30448    
30449 };
30450
30451 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30452     
30453     rid : '',
30454     active : false,
30455     disabled : false,
30456     html : '',
30457     position : 'bottom',
30458     icon : false,
30459     
30460     getAutoCreate : function()
30461     {
30462         var iconCls = 'roo-navigation-bar-item-icon';
30463         
30464         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30465         
30466         var cfg = {
30467             tag: 'li',
30468             cls: 'roo-navigation-bar-item',
30469             cn : [
30470                 {
30471                     tag : 'i',
30472                     cls : iconCls
30473                 }
30474             ]
30475         };
30476         
30477         if(this.active){
30478             cfg.cls += ' active';
30479         }
30480         if(this.disabled){
30481             cfg.cls += ' disabled';
30482         }
30483         
30484         return cfg;
30485     },
30486     
30487     disable : function()
30488     {
30489         this.setDisabled(true);
30490     },
30491     
30492     enable : function()
30493     {
30494         this.setDisabled(false);
30495     },
30496     
30497     initEvents: function() 
30498     {
30499         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30500         
30501         this.iconEl.on('click', this.onClick, this);
30502     },
30503     
30504     onClick : function(e)
30505     {
30506         e.preventDefault();
30507         
30508         if(this.disabled){
30509             return;
30510         }
30511         
30512         if(this.fireEvent('click', this, e) === false){
30513             return;
30514         };
30515         
30516         this.parent().setActiveItem(this);
30517     },
30518     
30519     isActive: function () 
30520     {
30521         return this.active;
30522     },
30523     
30524     setActive : function(state)
30525     {
30526         if(this.active == state){
30527             return;
30528         }
30529         
30530         this.active = state;
30531         
30532         if (state) {
30533             this.el.addClass('active');
30534             return;
30535         }
30536         
30537         this.el.removeClass('active');
30538         
30539         return;
30540     },
30541     
30542     setDisabled : function(state)
30543     {
30544         if(this.disabled == state){
30545             return;
30546         }
30547         
30548         this.disabled = state;
30549         
30550         if (state) {
30551             this.el.addClass('disabled');
30552             return;
30553         }
30554         
30555         this.el.removeClass('disabled');
30556     },
30557     
30558     tooltipEl : function()
30559     {
30560         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30561     }
30562 });
30563  
30564
30565  /*
30566  * - LGPL
30567  *
30568  * FieldLabel
30569  * 
30570  */
30571
30572 /**
30573  * @class Roo.bootstrap.FieldLabel
30574  * @extends Roo.bootstrap.Component
30575  * Bootstrap FieldLabel class
30576  * @cfg {String} html contents of the element
30577  * @cfg {String} tag tag of the element default label
30578  * @cfg {String} cls class of the element
30579  * @cfg {String} target label target 
30580  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30581  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30582  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30583  * @cfg {String} iconTooltip default "This field is required"
30584  * @cfg {String} indicatorpos (left|right) default left
30585  * 
30586  * @constructor
30587  * Create a new FieldLabel
30588  * @param {Object} config The config object
30589  */
30590
30591 Roo.bootstrap.FieldLabel = function(config){
30592     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30593     
30594     this.addEvents({
30595             /**
30596              * @event invalid
30597              * Fires after the field has been marked as invalid.
30598              * @param {Roo.form.FieldLabel} this
30599              * @param {String} msg The validation message
30600              */
30601             invalid : true,
30602             /**
30603              * @event valid
30604              * Fires after the field has been validated with no errors.
30605              * @param {Roo.form.FieldLabel} this
30606              */
30607             valid : true
30608         });
30609 };
30610
30611 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30612     
30613     tag: 'label',
30614     cls: '',
30615     html: '',
30616     target: '',
30617     allowBlank : true,
30618     invalidClass : 'has-warning',
30619     validClass : 'has-success',
30620     iconTooltip : 'This field is required',
30621     indicatorpos : 'left',
30622     
30623     getAutoCreate : function(){
30624         
30625         var cls = "";
30626         if (!this.allowBlank) {
30627             cls  = "visible";
30628         }
30629         
30630         var cfg = {
30631             tag : this.tag,
30632             cls : 'roo-bootstrap-field-label ' + this.cls,
30633             for : this.target,
30634             cn : [
30635                 {
30636                     tag : 'i',
30637                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30638                     tooltip : this.iconTooltip
30639                 },
30640                 {
30641                     tag : 'span',
30642                     html : this.html
30643                 }
30644             ] 
30645         };
30646         
30647         if(this.indicatorpos == 'right'){
30648             var cfg = {
30649                 tag : this.tag,
30650                 cls : 'roo-bootstrap-field-label ' + this.cls,
30651                 for : this.target,
30652                 cn : [
30653                     {
30654                         tag : 'span',
30655                         html : this.html
30656                     },
30657                     {
30658                         tag : 'i',
30659                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30660                         tooltip : this.iconTooltip
30661                     }
30662                 ] 
30663             };
30664         }
30665         
30666         return cfg;
30667     },
30668     
30669     initEvents: function() 
30670     {
30671         Roo.bootstrap.Element.superclass.initEvents.call(this);
30672         
30673         this.indicator = this.indicatorEl();
30674         
30675         if(this.indicator){
30676             this.indicator.removeClass('visible');
30677             this.indicator.addClass('invisible');
30678         }
30679         
30680         Roo.bootstrap.FieldLabel.register(this);
30681     },
30682     
30683     indicatorEl : function()
30684     {
30685         var indicator = this.el.select('i.roo-required-indicator',true).first();
30686         
30687         if(!indicator){
30688             return false;
30689         }
30690         
30691         return indicator;
30692         
30693     },
30694     
30695     /**
30696      * Mark this field as valid
30697      */
30698     markValid : function()
30699     {
30700         if(this.indicator){
30701             this.indicator.removeClass('visible');
30702             this.indicator.addClass('invisible');
30703         }
30704         if (Roo.bootstrap.version == 3) {
30705             this.el.removeClass(this.invalidClass);
30706             this.el.addClass(this.validClass);
30707         } else {
30708             this.el.removeClass('is-invalid');
30709             this.el.addClass('is-valid');
30710         }
30711         
30712         
30713         this.fireEvent('valid', this);
30714     },
30715     
30716     /**
30717      * Mark this field as invalid
30718      * @param {String} msg The validation message
30719      */
30720     markInvalid : function(msg)
30721     {
30722         if(this.indicator){
30723             this.indicator.removeClass('invisible');
30724             this.indicator.addClass('visible');
30725         }
30726           if (Roo.bootstrap.version == 3) {
30727             this.el.removeClass(this.validClass);
30728             this.el.addClass(this.invalidClass);
30729         } else {
30730             this.el.removeClass('is-valid');
30731             this.el.addClass('is-invalid');
30732         }
30733         
30734         
30735         this.fireEvent('invalid', this, msg);
30736     }
30737     
30738    
30739 });
30740
30741 Roo.apply(Roo.bootstrap.FieldLabel, {
30742     
30743     groups: {},
30744     
30745      /**
30746     * register a FieldLabel Group
30747     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30748     */
30749     register : function(label)
30750     {
30751         if(this.groups.hasOwnProperty(label.target)){
30752             return;
30753         }
30754      
30755         this.groups[label.target] = label;
30756         
30757     },
30758     /**
30759     * fetch a FieldLabel Group based on the target
30760     * @param {string} target
30761     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30762     */
30763     get: function(target) {
30764         if (typeof(this.groups[target]) == 'undefined') {
30765             return false;
30766         }
30767         
30768         return this.groups[target] ;
30769     }
30770 });
30771
30772  
30773
30774  /*
30775  * - LGPL
30776  *
30777  * page DateSplitField.
30778  * 
30779  */
30780
30781
30782 /**
30783  * @class Roo.bootstrap.DateSplitField
30784  * @extends Roo.bootstrap.Component
30785  * Bootstrap DateSplitField class
30786  * @cfg {string} fieldLabel - the label associated
30787  * @cfg {Number} labelWidth set the width of label (0-12)
30788  * @cfg {String} labelAlign (top|left)
30789  * @cfg {Boolean} dayAllowBlank (true|false) default false
30790  * @cfg {Boolean} monthAllowBlank (true|false) default false
30791  * @cfg {Boolean} yearAllowBlank (true|false) default false
30792  * @cfg {string} dayPlaceholder 
30793  * @cfg {string} monthPlaceholder
30794  * @cfg {string} yearPlaceholder
30795  * @cfg {string} dayFormat default 'd'
30796  * @cfg {string} monthFormat default 'm'
30797  * @cfg {string} yearFormat default 'Y'
30798  * @cfg {Number} labellg set the width of label (1-12)
30799  * @cfg {Number} labelmd set the width of label (1-12)
30800  * @cfg {Number} labelsm set the width of label (1-12)
30801  * @cfg {Number} labelxs set the width of label (1-12)
30802
30803  *     
30804  * @constructor
30805  * Create a new DateSplitField
30806  * @param {Object} config The config object
30807  */
30808
30809 Roo.bootstrap.DateSplitField = function(config){
30810     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30811     
30812     this.addEvents({
30813         // raw events
30814          /**
30815          * @event years
30816          * getting the data of years
30817          * @param {Roo.bootstrap.DateSplitField} this
30818          * @param {Object} years
30819          */
30820         "years" : true,
30821         /**
30822          * @event days
30823          * getting the data of days
30824          * @param {Roo.bootstrap.DateSplitField} this
30825          * @param {Object} days
30826          */
30827         "days" : true,
30828         /**
30829          * @event invalid
30830          * Fires after the field has been marked as invalid.
30831          * @param {Roo.form.Field} this
30832          * @param {String} msg The validation message
30833          */
30834         invalid : true,
30835        /**
30836          * @event valid
30837          * Fires after the field has been validated with no errors.
30838          * @param {Roo.form.Field} this
30839          */
30840         valid : true
30841     });
30842 };
30843
30844 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30845     
30846     fieldLabel : '',
30847     labelAlign : 'top',
30848     labelWidth : 3,
30849     dayAllowBlank : false,
30850     monthAllowBlank : false,
30851     yearAllowBlank : false,
30852     dayPlaceholder : '',
30853     monthPlaceholder : '',
30854     yearPlaceholder : '',
30855     dayFormat : 'd',
30856     monthFormat : 'm',
30857     yearFormat : 'Y',
30858     isFormField : true,
30859     labellg : 0,
30860     labelmd : 0,
30861     labelsm : 0,
30862     labelxs : 0,
30863     
30864     getAutoCreate : function()
30865     {
30866         var cfg = {
30867             tag : 'div',
30868             cls : 'row roo-date-split-field-group',
30869             cn : [
30870                 {
30871                     tag : 'input',
30872                     type : 'hidden',
30873                     cls : 'form-hidden-field roo-date-split-field-group-value',
30874                     name : this.name
30875                 }
30876             ]
30877         };
30878         
30879         var labelCls = 'col-md-12';
30880         var contentCls = 'col-md-4';
30881         
30882         if(this.fieldLabel){
30883             
30884             var label = {
30885                 tag : 'div',
30886                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30887                 cn : [
30888                     {
30889                         tag : 'label',
30890                         html : this.fieldLabel
30891                     }
30892                 ]
30893             };
30894             
30895             if(this.labelAlign == 'left'){
30896             
30897                 if(this.labelWidth > 12){
30898                     label.style = "width: " + this.labelWidth + 'px';
30899                 }
30900
30901                 if(this.labelWidth < 13 && this.labelmd == 0){
30902                     this.labelmd = this.labelWidth;
30903                 }
30904
30905                 if(this.labellg > 0){
30906                     labelCls = ' col-lg-' + this.labellg;
30907                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30908                 }
30909
30910                 if(this.labelmd > 0){
30911                     labelCls = ' col-md-' + this.labelmd;
30912                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30913                 }
30914
30915                 if(this.labelsm > 0){
30916                     labelCls = ' col-sm-' + this.labelsm;
30917                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30918                 }
30919
30920                 if(this.labelxs > 0){
30921                     labelCls = ' col-xs-' + this.labelxs;
30922                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30923                 }
30924             }
30925             
30926             label.cls += ' ' + labelCls;
30927             
30928             cfg.cn.push(label);
30929         }
30930         
30931         Roo.each(['day', 'month', 'year'], function(t){
30932             cfg.cn.push({
30933                 tag : 'div',
30934                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30935             });
30936         }, this);
30937         
30938         return cfg;
30939     },
30940     
30941     inputEl: function ()
30942     {
30943         return this.el.select('.roo-date-split-field-group-value', true).first();
30944     },
30945     
30946     onRender : function(ct, position) 
30947     {
30948         var _this = this;
30949         
30950         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30951         
30952         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30953         
30954         this.dayField = new Roo.bootstrap.ComboBox({
30955             allowBlank : this.dayAllowBlank,
30956             alwaysQuery : true,
30957             displayField : 'value',
30958             editable : false,
30959             fieldLabel : '',
30960             forceSelection : true,
30961             mode : 'local',
30962             placeholder : this.dayPlaceholder,
30963             selectOnFocus : true,
30964             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30965             triggerAction : 'all',
30966             typeAhead : true,
30967             valueField : 'value',
30968             store : new Roo.data.SimpleStore({
30969                 data : (function() {    
30970                     var days = [];
30971                     _this.fireEvent('days', _this, days);
30972                     return days;
30973                 })(),
30974                 fields : [ 'value' ]
30975             }),
30976             listeners : {
30977                 select : function (_self, record, index)
30978                 {
30979                     _this.setValue(_this.getValue());
30980                 }
30981             }
30982         });
30983
30984         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30985         
30986         this.monthField = new Roo.bootstrap.MonthField({
30987             after : '<i class=\"fa fa-calendar\"></i>',
30988             allowBlank : this.monthAllowBlank,
30989             placeholder : this.monthPlaceholder,
30990             readOnly : true,
30991             listeners : {
30992                 render : function (_self)
30993                 {
30994                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30995                         e.preventDefault();
30996                         _self.focus();
30997                     });
30998                 },
30999                 select : function (_self, oldvalue, newvalue)
31000                 {
31001                     _this.setValue(_this.getValue());
31002                 }
31003             }
31004         });
31005         
31006         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
31007         
31008         this.yearField = new Roo.bootstrap.ComboBox({
31009             allowBlank : this.yearAllowBlank,
31010             alwaysQuery : true,
31011             displayField : 'value',
31012             editable : false,
31013             fieldLabel : '',
31014             forceSelection : true,
31015             mode : 'local',
31016             placeholder : this.yearPlaceholder,
31017             selectOnFocus : true,
31018             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
31019             triggerAction : 'all',
31020             typeAhead : true,
31021             valueField : 'value',
31022             store : new Roo.data.SimpleStore({
31023                 data : (function() {
31024                     var years = [];
31025                     _this.fireEvent('years', _this, years);
31026                     return years;
31027                 })(),
31028                 fields : [ 'value' ]
31029             }),
31030             listeners : {
31031                 select : function (_self, record, index)
31032                 {
31033                     _this.setValue(_this.getValue());
31034                 }
31035             }
31036         });
31037
31038         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
31039     },
31040     
31041     setValue : function(v, format)
31042     {
31043         this.inputEl.dom.value = v;
31044         
31045         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
31046         
31047         var d = Date.parseDate(v, f);
31048         
31049         if(!d){
31050             this.validate();
31051             return;
31052         }
31053         
31054         this.setDay(d.format(this.dayFormat));
31055         this.setMonth(d.format(this.monthFormat));
31056         this.setYear(d.format(this.yearFormat));
31057         
31058         this.validate();
31059         
31060         return;
31061     },
31062     
31063     setDay : function(v)
31064     {
31065         this.dayField.setValue(v);
31066         this.inputEl.dom.value = this.getValue();
31067         this.validate();
31068         return;
31069     },
31070     
31071     setMonth : function(v)
31072     {
31073         this.monthField.setValue(v, true);
31074         this.inputEl.dom.value = this.getValue();
31075         this.validate();
31076         return;
31077     },
31078     
31079     setYear : function(v)
31080     {
31081         this.yearField.setValue(v);
31082         this.inputEl.dom.value = this.getValue();
31083         this.validate();
31084         return;
31085     },
31086     
31087     getDay : function()
31088     {
31089         return this.dayField.getValue();
31090     },
31091     
31092     getMonth : function()
31093     {
31094         return this.monthField.getValue();
31095     },
31096     
31097     getYear : function()
31098     {
31099         return this.yearField.getValue();
31100     },
31101     
31102     getValue : function()
31103     {
31104         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31105         
31106         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31107         
31108         return date;
31109     },
31110     
31111     reset : function()
31112     {
31113         this.setDay('');
31114         this.setMonth('');
31115         this.setYear('');
31116         this.inputEl.dom.value = '';
31117         this.validate();
31118         return;
31119     },
31120     
31121     validate : function()
31122     {
31123         var d = this.dayField.validate();
31124         var m = this.monthField.validate();
31125         var y = this.yearField.validate();
31126         
31127         var valid = true;
31128         
31129         if(
31130                 (!this.dayAllowBlank && !d) ||
31131                 (!this.monthAllowBlank && !m) ||
31132                 (!this.yearAllowBlank && !y)
31133         ){
31134             valid = false;
31135         }
31136         
31137         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31138             return valid;
31139         }
31140         
31141         if(valid){
31142             this.markValid();
31143             return valid;
31144         }
31145         
31146         this.markInvalid();
31147         
31148         return valid;
31149     },
31150     
31151     markValid : function()
31152     {
31153         
31154         var label = this.el.select('label', true).first();
31155         var icon = this.el.select('i.fa-star', true).first();
31156
31157         if(label && icon){
31158             icon.remove();
31159         }
31160         
31161         this.fireEvent('valid', this);
31162     },
31163     
31164      /**
31165      * Mark this field as invalid
31166      * @param {String} msg The validation message
31167      */
31168     markInvalid : function(msg)
31169     {
31170         
31171         var label = this.el.select('label', true).first();
31172         var icon = this.el.select('i.fa-star', true).first();
31173
31174         if(label && !icon){
31175             this.el.select('.roo-date-split-field-label', true).createChild({
31176                 tag : 'i',
31177                 cls : 'text-danger fa fa-lg fa-star',
31178                 tooltip : 'This field is required',
31179                 style : 'margin-right:5px;'
31180             }, label, true);
31181         }
31182         
31183         this.fireEvent('invalid', this, msg);
31184     },
31185     
31186     clearInvalid : function()
31187     {
31188         var label = this.el.select('label', true).first();
31189         var icon = this.el.select('i.fa-star', true).first();
31190
31191         if(label && icon){
31192             icon.remove();
31193         }
31194         
31195         this.fireEvent('valid', this);
31196     },
31197     
31198     getName: function()
31199     {
31200         return this.name;
31201     }
31202     
31203 });
31204
31205  /**
31206  *
31207  * This is based on 
31208  * http://masonry.desandro.com
31209  *
31210  * The idea is to render all the bricks based on vertical width...
31211  *
31212  * The original code extends 'outlayer' - we might need to use that....
31213  * 
31214  */
31215
31216
31217 /**
31218  * @class Roo.bootstrap.LayoutMasonry
31219  * @extends Roo.bootstrap.Component
31220  * Bootstrap Layout Masonry class
31221  * 
31222  * @constructor
31223  * Create a new Element
31224  * @param {Object} config The config object
31225  */
31226
31227 Roo.bootstrap.LayoutMasonry = function(config){
31228     
31229     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31230     
31231     this.bricks = [];
31232     
31233     Roo.bootstrap.LayoutMasonry.register(this);
31234     
31235     this.addEvents({
31236         // raw events
31237         /**
31238          * @event layout
31239          * Fire after layout the items
31240          * @param {Roo.bootstrap.LayoutMasonry} this
31241          * @param {Roo.EventObject} e
31242          */
31243         "layout" : true
31244     });
31245     
31246 };
31247
31248 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31249     
31250     /**
31251      * @cfg {Boolean} isLayoutInstant = no animation?
31252      */   
31253     isLayoutInstant : false, // needed?
31254    
31255     /**
31256      * @cfg {Number} boxWidth  width of the columns
31257      */   
31258     boxWidth : 450,
31259     
31260       /**
31261      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31262      */   
31263     boxHeight : 0,
31264     
31265     /**
31266      * @cfg {Number} padWidth padding below box..
31267      */   
31268     padWidth : 10, 
31269     
31270     /**
31271      * @cfg {Number} gutter gutter width..
31272      */   
31273     gutter : 10,
31274     
31275      /**
31276      * @cfg {Number} maxCols maximum number of columns
31277      */   
31278     
31279     maxCols: 0,
31280     
31281     /**
31282      * @cfg {Boolean} isAutoInitial defalut true
31283      */   
31284     isAutoInitial : true, 
31285     
31286     containerWidth: 0,
31287     
31288     /**
31289      * @cfg {Boolean} isHorizontal defalut false
31290      */   
31291     isHorizontal : false, 
31292
31293     currentSize : null,
31294     
31295     tag: 'div',
31296     
31297     cls: '',
31298     
31299     bricks: null, //CompositeElement
31300     
31301     cols : 1,
31302     
31303     _isLayoutInited : false,
31304     
31305 //    isAlternative : false, // only use for vertical layout...
31306     
31307     /**
31308      * @cfg {Number} alternativePadWidth padding below box..
31309      */   
31310     alternativePadWidth : 50,
31311     
31312     selectedBrick : [],
31313     
31314     getAutoCreate : function(){
31315         
31316         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31317         
31318         var cfg = {
31319             tag: this.tag,
31320             cls: 'blog-masonary-wrapper ' + this.cls,
31321             cn : {
31322                 cls : 'mas-boxes masonary'
31323             }
31324         };
31325         
31326         return cfg;
31327     },
31328     
31329     getChildContainer: function( )
31330     {
31331         if (this.boxesEl) {
31332             return this.boxesEl;
31333         }
31334         
31335         this.boxesEl = this.el.select('.mas-boxes').first();
31336         
31337         return this.boxesEl;
31338     },
31339     
31340     
31341     initEvents : function()
31342     {
31343         var _this = this;
31344         
31345         if(this.isAutoInitial){
31346             Roo.log('hook children rendered');
31347             this.on('childrenrendered', function() {
31348                 Roo.log('children rendered');
31349                 _this.initial();
31350             } ,this);
31351         }
31352     },
31353     
31354     initial : function()
31355     {
31356         this.selectedBrick = [];
31357         
31358         this.currentSize = this.el.getBox(true);
31359         
31360         Roo.EventManager.onWindowResize(this.resize, this); 
31361
31362         if(!this.isAutoInitial){
31363             this.layout();
31364             return;
31365         }
31366         
31367         this.layout();
31368         
31369         return;
31370         //this.layout.defer(500,this);
31371         
31372     },
31373     
31374     resize : function()
31375     {
31376         var cs = this.el.getBox(true);
31377         
31378         if (
31379                 this.currentSize.width == cs.width && 
31380                 this.currentSize.x == cs.x && 
31381                 this.currentSize.height == cs.height && 
31382                 this.currentSize.y == cs.y 
31383         ) {
31384             Roo.log("no change in with or X or Y");
31385             return;
31386         }
31387         
31388         this.currentSize = cs;
31389         
31390         this.layout();
31391         
31392     },
31393     
31394     layout : function()
31395     {   
31396         this._resetLayout();
31397         
31398         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31399         
31400         this.layoutItems( isInstant );
31401       
31402         this._isLayoutInited = true;
31403         
31404         this.fireEvent('layout', this);
31405         
31406     },
31407     
31408     _resetLayout : function()
31409     {
31410         if(this.isHorizontal){
31411             this.horizontalMeasureColumns();
31412             return;
31413         }
31414         
31415         this.verticalMeasureColumns();
31416         
31417     },
31418     
31419     verticalMeasureColumns : function()
31420     {
31421         this.getContainerWidth();
31422         
31423 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31424 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31425 //            return;
31426 //        }
31427         
31428         var boxWidth = this.boxWidth + this.padWidth;
31429         
31430         if(this.containerWidth < this.boxWidth){
31431             boxWidth = this.containerWidth
31432         }
31433         
31434         var containerWidth = this.containerWidth;
31435         
31436         var cols = Math.floor(containerWidth / boxWidth);
31437         
31438         this.cols = Math.max( cols, 1 );
31439         
31440         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31441         
31442         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31443         
31444         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31445         
31446         this.colWidth = boxWidth + avail - this.padWidth;
31447         
31448         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31449         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31450     },
31451     
31452     horizontalMeasureColumns : function()
31453     {
31454         this.getContainerWidth();
31455         
31456         var boxWidth = this.boxWidth;
31457         
31458         if(this.containerWidth < boxWidth){
31459             boxWidth = this.containerWidth;
31460         }
31461         
31462         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31463         
31464         this.el.setHeight(boxWidth);
31465         
31466     },
31467     
31468     getContainerWidth : function()
31469     {
31470         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31471     },
31472     
31473     layoutItems : function( isInstant )
31474     {
31475         Roo.log(this.bricks);
31476         
31477         var items = Roo.apply([], this.bricks);
31478         
31479         if(this.isHorizontal){
31480             this._horizontalLayoutItems( items , isInstant );
31481             return;
31482         }
31483         
31484 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31485 //            this._verticalAlternativeLayoutItems( items , isInstant );
31486 //            return;
31487 //        }
31488         
31489         this._verticalLayoutItems( items , isInstant );
31490         
31491     },
31492     
31493     _verticalLayoutItems : function ( items , isInstant)
31494     {
31495         if ( !items || !items.length ) {
31496             return;
31497         }
31498         
31499         var standard = [
31500             ['xs', 'xs', 'xs', 'tall'],
31501             ['xs', 'xs', 'tall'],
31502             ['xs', 'xs', 'sm'],
31503             ['xs', 'xs', 'xs'],
31504             ['xs', 'tall'],
31505             ['xs', 'sm'],
31506             ['xs', 'xs'],
31507             ['xs'],
31508             
31509             ['sm', 'xs', 'xs'],
31510             ['sm', 'xs'],
31511             ['sm'],
31512             
31513             ['tall', 'xs', 'xs', 'xs'],
31514             ['tall', 'xs', 'xs'],
31515             ['tall', 'xs'],
31516             ['tall']
31517             
31518         ];
31519         
31520         var queue = [];
31521         
31522         var boxes = [];
31523         
31524         var box = [];
31525         
31526         Roo.each(items, function(item, k){
31527             
31528             switch (item.size) {
31529                 // these layouts take up a full box,
31530                 case 'md' :
31531                 case 'md-left' :
31532                 case 'md-right' :
31533                 case 'wide' :
31534                     
31535                     if(box.length){
31536                         boxes.push(box);
31537                         box = [];
31538                     }
31539                     
31540                     boxes.push([item]);
31541                     
31542                     break;
31543                     
31544                 case 'xs' :
31545                 case 'sm' :
31546                 case 'tall' :
31547                     
31548                     box.push(item);
31549                     
31550                     break;
31551                 default :
31552                     break;
31553                     
31554             }
31555             
31556         }, this);
31557         
31558         if(box.length){
31559             boxes.push(box);
31560             box = [];
31561         }
31562         
31563         var filterPattern = function(box, length)
31564         {
31565             if(!box.length){
31566                 return;
31567             }
31568             
31569             var match = false;
31570             
31571             var pattern = box.slice(0, length);
31572             
31573             var format = [];
31574             
31575             Roo.each(pattern, function(i){
31576                 format.push(i.size);
31577             }, this);
31578             
31579             Roo.each(standard, function(s){
31580                 
31581                 if(String(s) != String(format)){
31582                     return;
31583                 }
31584                 
31585                 match = true;
31586                 return false;
31587                 
31588             }, this);
31589             
31590             if(!match && length == 1){
31591                 return;
31592             }
31593             
31594             if(!match){
31595                 filterPattern(box, length - 1);
31596                 return;
31597             }
31598                 
31599             queue.push(pattern);
31600
31601             box = box.slice(length, box.length);
31602
31603             filterPattern(box, 4);
31604
31605             return;
31606             
31607         }
31608         
31609         Roo.each(boxes, function(box, k){
31610             
31611             if(!box.length){
31612                 return;
31613             }
31614             
31615             if(box.length == 1){
31616                 queue.push(box);
31617                 return;
31618             }
31619             
31620             filterPattern(box, 4);
31621             
31622         }, this);
31623         
31624         this._processVerticalLayoutQueue( queue, isInstant );
31625         
31626     },
31627     
31628 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31629 //    {
31630 //        if ( !items || !items.length ) {
31631 //            return;
31632 //        }
31633 //
31634 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31635 //        
31636 //    },
31637     
31638     _horizontalLayoutItems : function ( items , isInstant)
31639     {
31640         if ( !items || !items.length || items.length < 3) {
31641             return;
31642         }
31643         
31644         items.reverse();
31645         
31646         var eItems = items.slice(0, 3);
31647         
31648         items = items.slice(3, items.length);
31649         
31650         var standard = [
31651             ['xs', 'xs', 'xs', 'wide'],
31652             ['xs', 'xs', 'wide'],
31653             ['xs', 'xs', 'sm'],
31654             ['xs', 'xs', 'xs'],
31655             ['xs', 'wide'],
31656             ['xs', 'sm'],
31657             ['xs', 'xs'],
31658             ['xs'],
31659             
31660             ['sm', 'xs', 'xs'],
31661             ['sm', 'xs'],
31662             ['sm'],
31663             
31664             ['wide', 'xs', 'xs', 'xs'],
31665             ['wide', 'xs', 'xs'],
31666             ['wide', 'xs'],
31667             ['wide'],
31668             
31669             ['wide-thin']
31670         ];
31671         
31672         var queue = [];
31673         
31674         var boxes = [];
31675         
31676         var box = [];
31677         
31678         Roo.each(items, function(item, k){
31679             
31680             switch (item.size) {
31681                 case 'md' :
31682                 case 'md-left' :
31683                 case 'md-right' :
31684                 case 'tall' :
31685                     
31686                     if(box.length){
31687                         boxes.push(box);
31688                         box = [];
31689                     }
31690                     
31691                     boxes.push([item]);
31692                     
31693                     break;
31694                     
31695                 case 'xs' :
31696                 case 'sm' :
31697                 case 'wide' :
31698                 case 'wide-thin' :
31699                     
31700                     box.push(item);
31701                     
31702                     break;
31703                 default :
31704                     break;
31705                     
31706             }
31707             
31708         }, this);
31709         
31710         if(box.length){
31711             boxes.push(box);
31712             box = [];
31713         }
31714         
31715         var filterPattern = function(box, length)
31716         {
31717             if(!box.length){
31718                 return;
31719             }
31720             
31721             var match = false;
31722             
31723             var pattern = box.slice(0, length);
31724             
31725             var format = [];
31726             
31727             Roo.each(pattern, function(i){
31728                 format.push(i.size);
31729             }, this);
31730             
31731             Roo.each(standard, function(s){
31732                 
31733                 if(String(s) != String(format)){
31734                     return;
31735                 }
31736                 
31737                 match = true;
31738                 return false;
31739                 
31740             }, this);
31741             
31742             if(!match && length == 1){
31743                 return;
31744             }
31745             
31746             if(!match){
31747                 filterPattern(box, length - 1);
31748                 return;
31749             }
31750                 
31751             queue.push(pattern);
31752
31753             box = box.slice(length, box.length);
31754
31755             filterPattern(box, 4);
31756
31757             return;
31758             
31759         }
31760         
31761         Roo.each(boxes, function(box, k){
31762             
31763             if(!box.length){
31764                 return;
31765             }
31766             
31767             if(box.length == 1){
31768                 queue.push(box);
31769                 return;
31770             }
31771             
31772             filterPattern(box, 4);
31773             
31774         }, this);
31775         
31776         
31777         var prune = [];
31778         
31779         var pos = this.el.getBox(true);
31780         
31781         var minX = pos.x;
31782         
31783         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31784         
31785         var hit_end = false;
31786         
31787         Roo.each(queue, function(box){
31788             
31789             if(hit_end){
31790                 
31791                 Roo.each(box, function(b){
31792                 
31793                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31794                     b.el.hide();
31795
31796                 }, this);
31797
31798                 return;
31799             }
31800             
31801             var mx = 0;
31802             
31803             Roo.each(box, function(b){
31804                 
31805                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31806                 b.el.show();
31807
31808                 mx = Math.max(mx, b.x);
31809                 
31810             }, this);
31811             
31812             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31813             
31814             if(maxX < minX){
31815                 
31816                 Roo.each(box, function(b){
31817                 
31818                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31819                     b.el.hide();
31820                     
31821                 }, this);
31822                 
31823                 hit_end = true;
31824                 
31825                 return;
31826             }
31827             
31828             prune.push(box);
31829             
31830         }, this);
31831         
31832         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31833     },
31834     
31835     /** Sets position of item in DOM
31836     * @param {Element} item
31837     * @param {Number} x - horizontal position
31838     * @param {Number} y - vertical position
31839     * @param {Boolean} isInstant - disables transitions
31840     */
31841     _processVerticalLayoutQueue : function( queue, isInstant )
31842     {
31843         var pos = this.el.getBox(true);
31844         var x = pos.x;
31845         var y = pos.y;
31846         var maxY = [];
31847         
31848         for (var i = 0; i < this.cols; i++){
31849             maxY[i] = pos.y;
31850         }
31851         
31852         Roo.each(queue, function(box, k){
31853             
31854             var col = k % this.cols;
31855             
31856             Roo.each(box, function(b,kk){
31857                 
31858                 b.el.position('absolute');
31859                 
31860                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31861                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31862                 
31863                 if(b.size == 'md-left' || b.size == 'md-right'){
31864                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31865                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31866                 }
31867                 
31868                 b.el.setWidth(width);
31869                 b.el.setHeight(height);
31870                 // iframe?
31871                 b.el.select('iframe',true).setSize(width,height);
31872                 
31873             }, this);
31874             
31875             for (var i = 0; i < this.cols; i++){
31876                 
31877                 if(maxY[i] < maxY[col]){
31878                     col = i;
31879                     continue;
31880                 }
31881                 
31882                 col = Math.min(col, i);
31883                 
31884             }
31885             
31886             x = pos.x + col * (this.colWidth + this.padWidth);
31887             
31888             y = maxY[col];
31889             
31890             var positions = [];
31891             
31892             switch (box.length){
31893                 case 1 :
31894                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31895                     break;
31896                 case 2 :
31897                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31898                     break;
31899                 case 3 :
31900                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31901                     break;
31902                 case 4 :
31903                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31904                     break;
31905                 default :
31906                     break;
31907             }
31908             
31909             Roo.each(box, function(b,kk){
31910                 
31911                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31912                 
31913                 var sz = b.el.getSize();
31914                 
31915                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31916                 
31917             }, this);
31918             
31919         }, this);
31920         
31921         var mY = 0;
31922         
31923         for (var i = 0; i < this.cols; i++){
31924             mY = Math.max(mY, maxY[i]);
31925         }
31926         
31927         this.el.setHeight(mY - pos.y);
31928         
31929     },
31930     
31931 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31932 //    {
31933 //        var pos = this.el.getBox(true);
31934 //        var x = pos.x;
31935 //        var y = pos.y;
31936 //        var maxX = pos.right;
31937 //        
31938 //        var maxHeight = 0;
31939 //        
31940 //        Roo.each(items, function(item, k){
31941 //            
31942 //            var c = k % 2;
31943 //            
31944 //            item.el.position('absolute');
31945 //                
31946 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31947 //
31948 //            item.el.setWidth(width);
31949 //
31950 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31951 //
31952 //            item.el.setHeight(height);
31953 //            
31954 //            if(c == 0){
31955 //                item.el.setXY([x, y], isInstant ? false : true);
31956 //            } else {
31957 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31958 //            }
31959 //            
31960 //            y = y + height + this.alternativePadWidth;
31961 //            
31962 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31963 //            
31964 //        }, this);
31965 //        
31966 //        this.el.setHeight(maxHeight);
31967 //        
31968 //    },
31969     
31970     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31971     {
31972         var pos = this.el.getBox(true);
31973         
31974         var minX = pos.x;
31975         var minY = pos.y;
31976         
31977         var maxX = pos.right;
31978         
31979         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31980         
31981         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31982         
31983         Roo.each(queue, function(box, k){
31984             
31985             Roo.each(box, function(b, kk){
31986                 
31987                 b.el.position('absolute');
31988                 
31989                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31990                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31991                 
31992                 if(b.size == 'md-left' || b.size == 'md-right'){
31993                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31994                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31995                 }
31996                 
31997                 b.el.setWidth(width);
31998                 b.el.setHeight(height);
31999                 
32000             }, this);
32001             
32002             if(!box.length){
32003                 return;
32004             }
32005             
32006             var positions = [];
32007             
32008             switch (box.length){
32009                 case 1 :
32010                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
32011                     break;
32012                 case 2 :
32013                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
32014                     break;
32015                 case 3 :
32016                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
32017                     break;
32018                 case 4 :
32019                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
32020                     break;
32021                 default :
32022                     break;
32023             }
32024             
32025             Roo.each(box, function(b,kk){
32026                 
32027                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
32028                 
32029                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
32030                 
32031             }, this);
32032             
32033         }, this);
32034         
32035     },
32036     
32037     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
32038     {
32039         Roo.each(eItems, function(b,k){
32040             
32041             b.size = (k == 0) ? 'sm' : 'xs';
32042             b.x = (k == 0) ? 2 : 1;
32043             b.y = (k == 0) ? 2 : 1;
32044             
32045             b.el.position('absolute');
32046             
32047             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
32048                 
32049             b.el.setWidth(width);
32050             
32051             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
32052             
32053             b.el.setHeight(height);
32054             
32055         }, this);
32056
32057         var positions = [];
32058         
32059         positions.push({
32060             x : maxX - this.unitWidth * 2 - this.gutter,
32061             y : minY
32062         });
32063         
32064         positions.push({
32065             x : maxX - this.unitWidth,
32066             y : minY + (this.unitWidth + this.gutter) * 2
32067         });
32068         
32069         positions.push({
32070             x : maxX - this.unitWidth * 3 - this.gutter * 2,
32071             y : minY
32072         });
32073         
32074         Roo.each(eItems, function(b,k){
32075             
32076             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
32077
32078         }, this);
32079         
32080     },
32081     
32082     getVerticalOneBoxColPositions : function(x, y, box)
32083     {
32084         var pos = [];
32085         
32086         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
32087         
32088         if(box[0].size == 'md-left'){
32089             rand = 0;
32090         }
32091         
32092         if(box[0].size == 'md-right'){
32093             rand = 1;
32094         }
32095         
32096         pos.push({
32097             x : x + (this.unitWidth + this.gutter) * rand,
32098             y : y
32099         });
32100         
32101         return pos;
32102     },
32103     
32104     getVerticalTwoBoxColPositions : function(x, y, box)
32105     {
32106         var pos = [];
32107         
32108         if(box[0].size == 'xs'){
32109             
32110             pos.push({
32111                 x : x,
32112                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32113             });
32114
32115             pos.push({
32116                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32117                 y : y
32118             });
32119             
32120             return pos;
32121             
32122         }
32123         
32124         pos.push({
32125             x : x,
32126             y : y
32127         });
32128
32129         pos.push({
32130             x : x + (this.unitWidth + this.gutter) * 2,
32131             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32132         });
32133         
32134         return pos;
32135         
32136     },
32137     
32138     getVerticalThreeBoxColPositions : function(x, y, box)
32139     {
32140         var pos = [];
32141         
32142         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32143             
32144             pos.push({
32145                 x : x,
32146                 y : y
32147             });
32148
32149             pos.push({
32150                 x : x + (this.unitWidth + this.gutter) * 1,
32151                 y : y
32152             });
32153             
32154             pos.push({
32155                 x : x + (this.unitWidth + this.gutter) * 2,
32156                 y : y
32157             });
32158             
32159             return pos;
32160             
32161         }
32162         
32163         if(box[0].size == 'xs' && box[1].size == 'xs'){
32164             
32165             pos.push({
32166                 x : x,
32167                 y : y
32168             });
32169
32170             pos.push({
32171                 x : x,
32172                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32173             });
32174             
32175             pos.push({
32176                 x : x + (this.unitWidth + this.gutter) * 1,
32177                 y : y
32178             });
32179             
32180             return pos;
32181             
32182         }
32183         
32184         pos.push({
32185             x : x,
32186             y : y
32187         });
32188
32189         pos.push({
32190             x : x + (this.unitWidth + this.gutter) * 2,
32191             y : y
32192         });
32193
32194         pos.push({
32195             x : x + (this.unitWidth + this.gutter) * 2,
32196             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32197         });
32198             
32199         return pos;
32200         
32201     },
32202     
32203     getVerticalFourBoxColPositions : function(x, y, box)
32204     {
32205         var pos = [];
32206         
32207         if(box[0].size == 'xs'){
32208             
32209             pos.push({
32210                 x : x,
32211                 y : y
32212             });
32213
32214             pos.push({
32215                 x : x,
32216                 y : y + (this.unitHeight + this.gutter) * 1
32217             });
32218             
32219             pos.push({
32220                 x : x,
32221                 y : y + (this.unitHeight + this.gutter) * 2
32222             });
32223             
32224             pos.push({
32225                 x : x + (this.unitWidth + this.gutter) * 1,
32226                 y : y
32227             });
32228             
32229             return pos;
32230             
32231         }
32232         
32233         pos.push({
32234             x : x,
32235             y : y
32236         });
32237
32238         pos.push({
32239             x : x + (this.unitWidth + this.gutter) * 2,
32240             y : y
32241         });
32242
32243         pos.push({
32244             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32245             y : y + (this.unitHeight + this.gutter) * 1
32246         });
32247
32248         pos.push({
32249             x : x + (this.unitWidth + this.gutter) * 2,
32250             y : y + (this.unitWidth + this.gutter) * 2
32251         });
32252
32253         return pos;
32254         
32255     },
32256     
32257     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32258     {
32259         var pos = [];
32260         
32261         if(box[0].size == 'md-left'){
32262             pos.push({
32263                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32264                 y : minY
32265             });
32266             
32267             return pos;
32268         }
32269         
32270         if(box[0].size == 'md-right'){
32271             pos.push({
32272                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32273                 y : minY + (this.unitWidth + this.gutter) * 1
32274             });
32275             
32276             return pos;
32277         }
32278         
32279         var rand = Math.floor(Math.random() * (4 - box[0].y));
32280         
32281         pos.push({
32282             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32283             y : minY + (this.unitWidth + this.gutter) * rand
32284         });
32285         
32286         return pos;
32287         
32288     },
32289     
32290     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32291     {
32292         var pos = [];
32293         
32294         if(box[0].size == 'xs'){
32295             
32296             pos.push({
32297                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32298                 y : minY
32299             });
32300
32301             pos.push({
32302                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32303                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32304             });
32305             
32306             return pos;
32307             
32308         }
32309         
32310         pos.push({
32311             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32312             y : minY
32313         });
32314
32315         pos.push({
32316             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32317             y : minY + (this.unitWidth + this.gutter) * 2
32318         });
32319         
32320         return pos;
32321         
32322     },
32323     
32324     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32325     {
32326         var pos = [];
32327         
32328         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32329             
32330             pos.push({
32331                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32332                 y : minY
32333             });
32334
32335             pos.push({
32336                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32337                 y : minY + (this.unitWidth + this.gutter) * 1
32338             });
32339             
32340             pos.push({
32341                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32342                 y : minY + (this.unitWidth + this.gutter) * 2
32343             });
32344             
32345             return pos;
32346             
32347         }
32348         
32349         if(box[0].size == 'xs' && box[1].size == 'xs'){
32350             
32351             pos.push({
32352                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32353                 y : minY
32354             });
32355
32356             pos.push({
32357                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32358                 y : minY
32359             });
32360             
32361             pos.push({
32362                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32363                 y : minY + (this.unitWidth + this.gutter) * 1
32364             });
32365             
32366             return pos;
32367             
32368         }
32369         
32370         pos.push({
32371             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32372             y : minY
32373         });
32374
32375         pos.push({
32376             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32377             y : minY + (this.unitWidth + this.gutter) * 2
32378         });
32379
32380         pos.push({
32381             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32382             y : minY + (this.unitWidth + this.gutter) * 2
32383         });
32384             
32385         return pos;
32386         
32387     },
32388     
32389     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32390     {
32391         var pos = [];
32392         
32393         if(box[0].size == 'xs'){
32394             
32395             pos.push({
32396                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32397                 y : minY
32398             });
32399
32400             pos.push({
32401                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32402                 y : minY
32403             });
32404             
32405             pos.push({
32406                 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),
32407                 y : minY
32408             });
32409             
32410             pos.push({
32411                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32412                 y : minY + (this.unitWidth + this.gutter) * 1
32413             });
32414             
32415             return pos;
32416             
32417         }
32418         
32419         pos.push({
32420             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32421             y : minY
32422         });
32423         
32424         pos.push({
32425             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32426             y : minY + (this.unitWidth + this.gutter) * 2
32427         });
32428         
32429         pos.push({
32430             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32431             y : minY + (this.unitWidth + this.gutter) * 2
32432         });
32433         
32434         pos.push({
32435             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),
32436             y : minY + (this.unitWidth + this.gutter) * 2
32437         });
32438
32439         return pos;
32440         
32441     },
32442     
32443     /**
32444     * remove a Masonry Brick
32445     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32446     */
32447     removeBrick : function(brick_id)
32448     {
32449         if (!brick_id) {
32450             return;
32451         }
32452         
32453         for (var i = 0; i<this.bricks.length; i++) {
32454             if (this.bricks[i].id == brick_id) {
32455                 this.bricks.splice(i,1);
32456                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32457                 this.initial();
32458             }
32459         }
32460     },
32461     
32462     /**
32463     * adds a Masonry Brick
32464     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32465     */
32466     addBrick : function(cfg)
32467     {
32468         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32469         //this.register(cn);
32470         cn.parentId = this.id;
32471         cn.render(this.el);
32472         return cn;
32473     },
32474     
32475     /**
32476     * register a Masonry Brick
32477     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32478     */
32479     
32480     register : function(brick)
32481     {
32482         this.bricks.push(brick);
32483         brick.masonryId = this.id;
32484     },
32485     
32486     /**
32487     * clear all the Masonry Brick
32488     */
32489     clearAll : function()
32490     {
32491         this.bricks = [];
32492         //this.getChildContainer().dom.innerHTML = "";
32493         this.el.dom.innerHTML = '';
32494     },
32495     
32496     getSelected : function()
32497     {
32498         if (!this.selectedBrick) {
32499             return false;
32500         }
32501         
32502         return this.selectedBrick;
32503     }
32504 });
32505
32506 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32507     
32508     groups: {},
32509      /**
32510     * register a Masonry Layout
32511     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32512     */
32513     
32514     register : function(layout)
32515     {
32516         this.groups[layout.id] = layout;
32517     },
32518     /**
32519     * fetch a  Masonry Layout based on the masonry layout ID
32520     * @param {string} the masonry layout to add
32521     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32522     */
32523     
32524     get: function(layout_id) {
32525         if (typeof(this.groups[layout_id]) == 'undefined') {
32526             return false;
32527         }
32528         return this.groups[layout_id] ;
32529     }
32530     
32531     
32532     
32533 });
32534
32535  
32536
32537  /**
32538  *
32539  * This is based on 
32540  * http://masonry.desandro.com
32541  *
32542  * The idea is to render all the bricks based on vertical width...
32543  *
32544  * The original code extends 'outlayer' - we might need to use that....
32545  * 
32546  */
32547
32548
32549 /**
32550  * @class Roo.bootstrap.LayoutMasonryAuto
32551  * @extends Roo.bootstrap.Component
32552  * Bootstrap Layout Masonry class
32553  * 
32554  * @constructor
32555  * Create a new Element
32556  * @param {Object} config The config object
32557  */
32558
32559 Roo.bootstrap.LayoutMasonryAuto = function(config){
32560     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32561 };
32562
32563 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32564     
32565       /**
32566      * @cfg {Boolean} isFitWidth  - resize the width..
32567      */   
32568     isFitWidth : false,  // options..
32569     /**
32570      * @cfg {Boolean} isOriginLeft = left align?
32571      */   
32572     isOriginLeft : true,
32573     /**
32574      * @cfg {Boolean} isOriginTop = top align?
32575      */   
32576     isOriginTop : false,
32577     /**
32578      * @cfg {Boolean} isLayoutInstant = no animation?
32579      */   
32580     isLayoutInstant : false, // needed?
32581     /**
32582      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32583      */   
32584     isResizingContainer : true,
32585     /**
32586      * @cfg {Number} columnWidth  width of the columns 
32587      */   
32588     
32589     columnWidth : 0,
32590     
32591     /**
32592      * @cfg {Number} maxCols maximum number of columns
32593      */   
32594     
32595     maxCols: 0,
32596     /**
32597      * @cfg {Number} padHeight padding below box..
32598      */   
32599     
32600     padHeight : 10, 
32601     
32602     /**
32603      * @cfg {Boolean} isAutoInitial defalut true
32604      */   
32605     
32606     isAutoInitial : true, 
32607     
32608     // private?
32609     gutter : 0,
32610     
32611     containerWidth: 0,
32612     initialColumnWidth : 0,
32613     currentSize : null,
32614     
32615     colYs : null, // array.
32616     maxY : 0,
32617     padWidth: 10,
32618     
32619     
32620     tag: 'div',
32621     cls: '',
32622     bricks: null, //CompositeElement
32623     cols : 0, // array?
32624     // element : null, // wrapped now this.el
32625     _isLayoutInited : null, 
32626     
32627     
32628     getAutoCreate : function(){
32629         
32630         var cfg = {
32631             tag: this.tag,
32632             cls: 'blog-masonary-wrapper ' + this.cls,
32633             cn : {
32634                 cls : 'mas-boxes masonary'
32635             }
32636         };
32637         
32638         return cfg;
32639     },
32640     
32641     getChildContainer: function( )
32642     {
32643         if (this.boxesEl) {
32644             return this.boxesEl;
32645         }
32646         
32647         this.boxesEl = this.el.select('.mas-boxes').first();
32648         
32649         return this.boxesEl;
32650     },
32651     
32652     
32653     initEvents : function()
32654     {
32655         var _this = this;
32656         
32657         if(this.isAutoInitial){
32658             Roo.log('hook children rendered');
32659             this.on('childrenrendered', function() {
32660                 Roo.log('children rendered');
32661                 _this.initial();
32662             } ,this);
32663         }
32664         
32665     },
32666     
32667     initial : function()
32668     {
32669         this.reloadItems();
32670
32671         this.currentSize = this.el.getBox(true);
32672
32673         /// was window resize... - let's see if this works..
32674         Roo.EventManager.onWindowResize(this.resize, this); 
32675
32676         if(!this.isAutoInitial){
32677             this.layout();
32678             return;
32679         }
32680         
32681         this.layout.defer(500,this);
32682     },
32683     
32684     reloadItems: function()
32685     {
32686         this.bricks = this.el.select('.masonry-brick', true);
32687         
32688         this.bricks.each(function(b) {
32689             //Roo.log(b.getSize());
32690             if (!b.attr('originalwidth')) {
32691                 b.attr('originalwidth',  b.getSize().width);
32692             }
32693             
32694         });
32695         
32696         Roo.log(this.bricks.elements.length);
32697     },
32698     
32699     resize : function()
32700     {
32701         Roo.log('resize');
32702         var cs = this.el.getBox(true);
32703         
32704         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32705             Roo.log("no change in with or X");
32706             return;
32707         }
32708         this.currentSize = cs;
32709         this.layout();
32710     },
32711     
32712     layout : function()
32713     {
32714          Roo.log('layout');
32715         this._resetLayout();
32716         //this._manageStamps();
32717       
32718         // don't animate first layout
32719         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32720         this.layoutItems( isInstant );
32721       
32722         // flag for initalized
32723         this._isLayoutInited = true;
32724     },
32725     
32726     layoutItems : function( isInstant )
32727     {
32728         //var items = this._getItemsForLayout( this.items );
32729         // original code supports filtering layout items.. we just ignore it..
32730         
32731         this._layoutItems( this.bricks , isInstant );
32732       
32733         this._postLayout();
32734     },
32735     _layoutItems : function ( items , isInstant)
32736     {
32737        //this.fireEvent( 'layout', this, items );
32738     
32739
32740         if ( !items || !items.elements.length ) {
32741           // no items, emit event with empty array
32742             return;
32743         }
32744
32745         var queue = [];
32746         items.each(function(item) {
32747             Roo.log("layout item");
32748             Roo.log(item);
32749             // get x/y object from method
32750             var position = this._getItemLayoutPosition( item );
32751             // enqueue
32752             position.item = item;
32753             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32754             queue.push( position );
32755         }, this);
32756       
32757         this._processLayoutQueue( queue );
32758     },
32759     /** Sets position of item in DOM
32760     * @param {Element} item
32761     * @param {Number} x - horizontal position
32762     * @param {Number} y - vertical position
32763     * @param {Boolean} isInstant - disables transitions
32764     */
32765     _processLayoutQueue : function( queue )
32766     {
32767         for ( var i=0, len = queue.length; i < len; i++ ) {
32768             var obj = queue[i];
32769             obj.item.position('absolute');
32770             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32771         }
32772     },
32773       
32774     
32775     /**
32776     * Any logic you want to do after each layout,
32777     * i.e. size the container
32778     */
32779     _postLayout : function()
32780     {
32781         this.resizeContainer();
32782     },
32783     
32784     resizeContainer : function()
32785     {
32786         if ( !this.isResizingContainer ) {
32787             return;
32788         }
32789         var size = this._getContainerSize();
32790         if ( size ) {
32791             this.el.setSize(size.width,size.height);
32792             this.boxesEl.setSize(size.width,size.height);
32793         }
32794     },
32795     
32796     
32797     
32798     _resetLayout : function()
32799     {
32800         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32801         this.colWidth = this.el.getWidth();
32802         //this.gutter = this.el.getWidth(); 
32803         
32804         this.measureColumns();
32805
32806         // reset column Y
32807         var i = this.cols;
32808         this.colYs = [];
32809         while (i--) {
32810             this.colYs.push( 0 );
32811         }
32812     
32813         this.maxY = 0;
32814     },
32815
32816     measureColumns : function()
32817     {
32818         this.getContainerWidth();
32819       // if columnWidth is 0, default to outerWidth of first item
32820         if ( !this.columnWidth ) {
32821             var firstItem = this.bricks.first();
32822             Roo.log(firstItem);
32823             this.columnWidth  = this.containerWidth;
32824             if (firstItem && firstItem.attr('originalwidth') ) {
32825                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32826             }
32827             // columnWidth fall back to item of first element
32828             Roo.log("set column width?");
32829                         this.initialColumnWidth = this.columnWidth  ;
32830
32831             // if first elem has no width, default to size of container
32832             
32833         }
32834         
32835         
32836         if (this.initialColumnWidth) {
32837             this.columnWidth = this.initialColumnWidth;
32838         }
32839         
32840         
32841             
32842         // column width is fixed at the top - however if container width get's smaller we should
32843         // reduce it...
32844         
32845         // this bit calcs how man columns..
32846             
32847         var columnWidth = this.columnWidth += this.gutter;
32848       
32849         // calculate columns
32850         var containerWidth = this.containerWidth + this.gutter;
32851         
32852         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32853         // fix rounding errors, typically with gutters
32854         var excess = columnWidth - containerWidth % columnWidth;
32855         
32856         
32857         // if overshoot is less than a pixel, round up, otherwise floor it
32858         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32859         cols = Math[ mathMethod ]( cols );
32860         this.cols = Math.max( cols, 1 );
32861         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32862         
32863          // padding positioning..
32864         var totalColWidth = this.cols * this.columnWidth;
32865         var padavail = this.containerWidth - totalColWidth;
32866         // so for 2 columns - we need 3 'pads'
32867         
32868         var padNeeded = (1+this.cols) * this.padWidth;
32869         
32870         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32871         
32872         this.columnWidth += padExtra
32873         //this.padWidth = Math.floor(padavail /  ( this.cols));
32874         
32875         // adjust colum width so that padding is fixed??
32876         
32877         // we have 3 columns ... total = width * 3
32878         // we have X left over... that should be used by 
32879         
32880         //if (this.expandC) {
32881             
32882         //}
32883         
32884         
32885         
32886     },
32887     
32888     getContainerWidth : function()
32889     {
32890        /* // container is parent if fit width
32891         var container = this.isFitWidth ? this.element.parentNode : this.element;
32892         // check that this.size and size are there
32893         // IE8 triggers resize on body size change, so they might not be
32894         
32895         var size = getSize( container );  //FIXME
32896         this.containerWidth = size && size.innerWidth; //FIXME
32897         */
32898          
32899         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32900         
32901     },
32902     
32903     _getItemLayoutPosition : function( item )  // what is item?
32904     {
32905         // we resize the item to our columnWidth..
32906       
32907         item.setWidth(this.columnWidth);
32908         item.autoBoxAdjust  = false;
32909         
32910         var sz = item.getSize();
32911  
32912         // how many columns does this brick span
32913         var remainder = this.containerWidth % this.columnWidth;
32914         
32915         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32916         // round if off by 1 pixel, otherwise use ceil
32917         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32918         colSpan = Math.min( colSpan, this.cols );
32919         
32920         // normally this should be '1' as we dont' currently allow multi width columns..
32921         
32922         var colGroup = this._getColGroup( colSpan );
32923         // get the minimum Y value from the columns
32924         var minimumY = Math.min.apply( Math, colGroup );
32925         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32926         
32927         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32928          
32929         // position the brick
32930         var position = {
32931             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32932             y: this.currentSize.y + minimumY + this.padHeight
32933         };
32934         
32935         Roo.log(position);
32936         // apply setHeight to necessary columns
32937         var setHeight = minimumY + sz.height + this.padHeight;
32938         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32939         
32940         var setSpan = this.cols + 1 - colGroup.length;
32941         for ( var i = 0; i < setSpan; i++ ) {
32942           this.colYs[ shortColIndex + i ] = setHeight ;
32943         }
32944       
32945         return position;
32946     },
32947     
32948     /**
32949      * @param {Number} colSpan - number of columns the element spans
32950      * @returns {Array} colGroup
32951      */
32952     _getColGroup : function( colSpan )
32953     {
32954         if ( colSpan < 2 ) {
32955           // if brick spans only one column, use all the column Ys
32956           return this.colYs;
32957         }
32958       
32959         var colGroup = [];
32960         // how many different places could this brick fit horizontally
32961         var groupCount = this.cols + 1 - colSpan;
32962         // for each group potential horizontal position
32963         for ( var i = 0; i < groupCount; i++ ) {
32964           // make an array of colY values for that one group
32965           var groupColYs = this.colYs.slice( i, i + colSpan );
32966           // and get the max value of the array
32967           colGroup[i] = Math.max.apply( Math, groupColYs );
32968         }
32969         return colGroup;
32970     },
32971     /*
32972     _manageStamp : function( stamp )
32973     {
32974         var stampSize =  stamp.getSize();
32975         var offset = stamp.getBox();
32976         // get the columns that this stamp affects
32977         var firstX = this.isOriginLeft ? offset.x : offset.right;
32978         var lastX = firstX + stampSize.width;
32979         var firstCol = Math.floor( firstX / this.columnWidth );
32980         firstCol = Math.max( 0, firstCol );
32981         
32982         var lastCol = Math.floor( lastX / this.columnWidth );
32983         // lastCol should not go over if multiple of columnWidth #425
32984         lastCol -= lastX % this.columnWidth ? 0 : 1;
32985         lastCol = Math.min( this.cols - 1, lastCol );
32986         
32987         // set colYs to bottom of the stamp
32988         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32989             stampSize.height;
32990             
32991         for ( var i = firstCol; i <= lastCol; i++ ) {
32992           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32993         }
32994     },
32995     */
32996     
32997     _getContainerSize : function()
32998     {
32999         this.maxY = Math.max.apply( Math, this.colYs );
33000         var size = {
33001             height: this.maxY
33002         };
33003       
33004         if ( this.isFitWidth ) {
33005             size.width = this._getContainerFitWidth();
33006         }
33007       
33008         return size;
33009     },
33010     
33011     _getContainerFitWidth : function()
33012     {
33013         var unusedCols = 0;
33014         // count unused columns
33015         var i = this.cols;
33016         while ( --i ) {
33017           if ( this.colYs[i] !== 0 ) {
33018             break;
33019           }
33020           unusedCols++;
33021         }
33022         // fit container to columns that have been used
33023         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
33024     },
33025     
33026     needsResizeLayout : function()
33027     {
33028         var previousWidth = this.containerWidth;
33029         this.getContainerWidth();
33030         return previousWidth !== this.containerWidth;
33031     }
33032  
33033 });
33034
33035  
33036
33037  /*
33038  * - LGPL
33039  *
33040  * element
33041  * 
33042  */
33043
33044 /**
33045  * @class Roo.bootstrap.MasonryBrick
33046  * @extends Roo.bootstrap.Component
33047  * Bootstrap MasonryBrick class
33048  * 
33049  * @constructor
33050  * Create a new MasonryBrick
33051  * @param {Object} config The config object
33052  */
33053
33054 Roo.bootstrap.MasonryBrick = function(config){
33055     
33056     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
33057     
33058     Roo.bootstrap.MasonryBrick.register(this);
33059     
33060     this.addEvents({
33061         // raw events
33062         /**
33063          * @event click
33064          * When a MasonryBrick is clcik
33065          * @param {Roo.bootstrap.MasonryBrick} this
33066          * @param {Roo.EventObject} e
33067          */
33068         "click" : true
33069     });
33070 };
33071
33072 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
33073     
33074     /**
33075      * @cfg {String} title
33076      */   
33077     title : '',
33078     /**
33079      * @cfg {String} html
33080      */   
33081     html : '',
33082     /**
33083      * @cfg {String} bgimage
33084      */   
33085     bgimage : '',
33086     /**
33087      * @cfg {String} videourl
33088      */   
33089     videourl : '',
33090     /**
33091      * @cfg {String} cls
33092      */   
33093     cls : '',
33094     /**
33095      * @cfg {String} href
33096      */   
33097     href : '',
33098     /**
33099      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33100      */   
33101     size : 'xs',
33102     
33103     /**
33104      * @cfg {String} placetitle (center|bottom)
33105      */   
33106     placetitle : '',
33107     
33108     /**
33109      * @cfg {Boolean} isFitContainer defalut true
33110      */   
33111     isFitContainer : true, 
33112     
33113     /**
33114      * @cfg {Boolean} preventDefault defalut false
33115      */   
33116     preventDefault : false, 
33117     
33118     /**
33119      * @cfg {Boolean} inverse defalut false
33120      */   
33121     maskInverse : false, 
33122     
33123     getAutoCreate : function()
33124     {
33125         if(!this.isFitContainer){
33126             return this.getSplitAutoCreate();
33127         }
33128         
33129         var cls = 'masonry-brick masonry-brick-full';
33130         
33131         if(this.href.length){
33132             cls += ' masonry-brick-link';
33133         }
33134         
33135         if(this.bgimage.length){
33136             cls += ' masonry-brick-image';
33137         }
33138         
33139         if(this.maskInverse){
33140             cls += ' mask-inverse';
33141         }
33142         
33143         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33144             cls += ' enable-mask';
33145         }
33146         
33147         if(this.size){
33148             cls += ' masonry-' + this.size + '-brick';
33149         }
33150         
33151         if(this.placetitle.length){
33152             
33153             switch (this.placetitle) {
33154                 case 'center' :
33155                     cls += ' masonry-center-title';
33156                     break;
33157                 case 'bottom' :
33158                     cls += ' masonry-bottom-title';
33159                     break;
33160                 default:
33161                     break;
33162             }
33163             
33164         } else {
33165             if(!this.html.length && !this.bgimage.length){
33166                 cls += ' masonry-center-title';
33167             }
33168
33169             if(!this.html.length && this.bgimage.length){
33170                 cls += ' masonry-bottom-title';
33171             }
33172         }
33173         
33174         if(this.cls){
33175             cls += ' ' + this.cls;
33176         }
33177         
33178         var cfg = {
33179             tag: (this.href.length) ? 'a' : 'div',
33180             cls: cls,
33181             cn: [
33182                 {
33183                     tag: 'div',
33184                     cls: 'masonry-brick-mask'
33185                 },
33186                 {
33187                     tag: 'div',
33188                     cls: 'masonry-brick-paragraph',
33189                     cn: []
33190                 }
33191             ]
33192         };
33193         
33194         if(this.href.length){
33195             cfg.href = this.href;
33196         }
33197         
33198         var cn = cfg.cn[1].cn;
33199         
33200         if(this.title.length){
33201             cn.push({
33202                 tag: 'h4',
33203                 cls: 'masonry-brick-title',
33204                 html: this.title
33205             });
33206         }
33207         
33208         if(this.html.length){
33209             cn.push({
33210                 tag: 'p',
33211                 cls: 'masonry-brick-text',
33212                 html: this.html
33213             });
33214         }
33215         
33216         if (!this.title.length && !this.html.length) {
33217             cfg.cn[1].cls += ' hide';
33218         }
33219         
33220         if(this.bgimage.length){
33221             cfg.cn.push({
33222                 tag: 'img',
33223                 cls: 'masonry-brick-image-view',
33224                 src: this.bgimage
33225             });
33226         }
33227         
33228         if(this.videourl.length){
33229             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33230             // youtube support only?
33231             cfg.cn.push({
33232                 tag: 'iframe',
33233                 cls: 'masonry-brick-image-view',
33234                 src: vurl,
33235                 frameborder : 0,
33236                 allowfullscreen : true
33237             });
33238         }
33239         
33240         return cfg;
33241         
33242     },
33243     
33244     getSplitAutoCreate : function()
33245     {
33246         var cls = 'masonry-brick masonry-brick-split';
33247         
33248         if(this.href.length){
33249             cls += ' masonry-brick-link';
33250         }
33251         
33252         if(this.bgimage.length){
33253             cls += ' masonry-brick-image';
33254         }
33255         
33256         if(this.size){
33257             cls += ' masonry-' + this.size + '-brick';
33258         }
33259         
33260         switch (this.placetitle) {
33261             case 'center' :
33262                 cls += ' masonry-center-title';
33263                 break;
33264             case 'bottom' :
33265                 cls += ' masonry-bottom-title';
33266                 break;
33267             default:
33268                 if(!this.bgimage.length){
33269                     cls += ' masonry-center-title';
33270                 }
33271
33272                 if(this.bgimage.length){
33273                     cls += ' masonry-bottom-title';
33274                 }
33275                 break;
33276         }
33277         
33278         if(this.cls){
33279             cls += ' ' + this.cls;
33280         }
33281         
33282         var cfg = {
33283             tag: (this.href.length) ? 'a' : 'div',
33284             cls: cls,
33285             cn: [
33286                 {
33287                     tag: 'div',
33288                     cls: 'masonry-brick-split-head',
33289                     cn: [
33290                         {
33291                             tag: 'div',
33292                             cls: 'masonry-brick-paragraph',
33293                             cn: []
33294                         }
33295                     ]
33296                 },
33297                 {
33298                     tag: 'div',
33299                     cls: 'masonry-brick-split-body',
33300                     cn: []
33301                 }
33302             ]
33303         };
33304         
33305         if(this.href.length){
33306             cfg.href = this.href;
33307         }
33308         
33309         if(this.title.length){
33310             cfg.cn[0].cn[0].cn.push({
33311                 tag: 'h4',
33312                 cls: 'masonry-brick-title',
33313                 html: this.title
33314             });
33315         }
33316         
33317         if(this.html.length){
33318             cfg.cn[1].cn.push({
33319                 tag: 'p',
33320                 cls: 'masonry-brick-text',
33321                 html: this.html
33322             });
33323         }
33324
33325         if(this.bgimage.length){
33326             cfg.cn[0].cn.push({
33327                 tag: 'img',
33328                 cls: 'masonry-brick-image-view',
33329                 src: this.bgimage
33330             });
33331         }
33332         
33333         if(this.videourl.length){
33334             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33335             // youtube support only?
33336             cfg.cn[0].cn.cn.push({
33337                 tag: 'iframe',
33338                 cls: 'masonry-brick-image-view',
33339                 src: vurl,
33340                 frameborder : 0,
33341                 allowfullscreen : true
33342             });
33343         }
33344         
33345         return cfg;
33346     },
33347     
33348     initEvents: function() 
33349     {
33350         switch (this.size) {
33351             case 'xs' :
33352                 this.x = 1;
33353                 this.y = 1;
33354                 break;
33355             case 'sm' :
33356                 this.x = 2;
33357                 this.y = 2;
33358                 break;
33359             case 'md' :
33360             case 'md-left' :
33361             case 'md-right' :
33362                 this.x = 3;
33363                 this.y = 3;
33364                 break;
33365             case 'tall' :
33366                 this.x = 2;
33367                 this.y = 3;
33368                 break;
33369             case 'wide' :
33370                 this.x = 3;
33371                 this.y = 2;
33372                 break;
33373             case 'wide-thin' :
33374                 this.x = 3;
33375                 this.y = 1;
33376                 break;
33377                         
33378             default :
33379                 break;
33380         }
33381         
33382         if(Roo.isTouch){
33383             this.el.on('touchstart', this.onTouchStart, this);
33384             this.el.on('touchmove', this.onTouchMove, this);
33385             this.el.on('touchend', this.onTouchEnd, this);
33386             this.el.on('contextmenu', this.onContextMenu, this);
33387         } else {
33388             this.el.on('mouseenter'  ,this.enter, this);
33389             this.el.on('mouseleave', this.leave, this);
33390             this.el.on('click', this.onClick, this);
33391         }
33392         
33393         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33394             this.parent().bricks.push(this);   
33395         }
33396         
33397     },
33398     
33399     onClick: function(e, el)
33400     {
33401         var time = this.endTimer - this.startTimer;
33402         // Roo.log(e.preventDefault());
33403         if(Roo.isTouch){
33404             if(time > 1000){
33405                 e.preventDefault();
33406                 return;
33407             }
33408         }
33409         
33410         if(!this.preventDefault){
33411             return;
33412         }
33413         
33414         e.preventDefault();
33415         
33416         if (this.activeClass != '') {
33417             this.selectBrick();
33418         }
33419         
33420         this.fireEvent('click', this, e);
33421     },
33422     
33423     enter: function(e, el)
33424     {
33425         e.preventDefault();
33426         
33427         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33428             return;
33429         }
33430         
33431         if(this.bgimage.length && this.html.length){
33432             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33433         }
33434     },
33435     
33436     leave: function(e, el)
33437     {
33438         e.preventDefault();
33439         
33440         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33441             return;
33442         }
33443         
33444         if(this.bgimage.length && this.html.length){
33445             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33446         }
33447     },
33448     
33449     onTouchStart: function(e, el)
33450     {
33451 //        e.preventDefault();
33452         
33453         this.touchmoved = false;
33454         
33455         if(!this.isFitContainer){
33456             return;
33457         }
33458         
33459         if(!this.bgimage.length || !this.html.length){
33460             return;
33461         }
33462         
33463         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33464         
33465         this.timer = new Date().getTime();
33466         
33467     },
33468     
33469     onTouchMove: function(e, el)
33470     {
33471         this.touchmoved = true;
33472     },
33473     
33474     onContextMenu : function(e,el)
33475     {
33476         e.preventDefault();
33477         e.stopPropagation();
33478         return false;
33479     },
33480     
33481     onTouchEnd: function(e, el)
33482     {
33483 //        e.preventDefault();
33484         
33485         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33486         
33487             this.leave(e,el);
33488             
33489             return;
33490         }
33491         
33492         if(!this.bgimage.length || !this.html.length){
33493             
33494             if(this.href.length){
33495                 window.location.href = this.href;
33496             }
33497             
33498             return;
33499         }
33500         
33501         if(!this.isFitContainer){
33502             return;
33503         }
33504         
33505         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33506         
33507         window.location.href = this.href;
33508     },
33509     
33510     //selection on single brick only
33511     selectBrick : function() {
33512         
33513         if (!this.parentId) {
33514             return;
33515         }
33516         
33517         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33518         var index = m.selectedBrick.indexOf(this.id);
33519         
33520         if ( index > -1) {
33521             m.selectedBrick.splice(index,1);
33522             this.el.removeClass(this.activeClass);
33523             return;
33524         }
33525         
33526         for(var i = 0; i < m.selectedBrick.length; i++) {
33527             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33528             b.el.removeClass(b.activeClass);
33529         }
33530         
33531         m.selectedBrick = [];
33532         
33533         m.selectedBrick.push(this.id);
33534         this.el.addClass(this.activeClass);
33535         return;
33536     },
33537     
33538     isSelected : function(){
33539         return this.el.hasClass(this.activeClass);
33540         
33541     }
33542 });
33543
33544 Roo.apply(Roo.bootstrap.MasonryBrick, {
33545     
33546     //groups: {},
33547     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33548      /**
33549     * register a Masonry Brick
33550     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33551     */
33552     
33553     register : function(brick)
33554     {
33555         //this.groups[brick.id] = brick;
33556         this.groups.add(brick.id, brick);
33557     },
33558     /**
33559     * fetch a  masonry brick based on the masonry brick ID
33560     * @param {string} the masonry brick to add
33561     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33562     */
33563     
33564     get: function(brick_id) 
33565     {
33566         // if (typeof(this.groups[brick_id]) == 'undefined') {
33567         //     return false;
33568         // }
33569         // return this.groups[brick_id] ;
33570         
33571         if(this.groups.key(brick_id)) {
33572             return this.groups.key(brick_id);
33573         }
33574         
33575         return false;
33576     }
33577     
33578     
33579     
33580 });
33581
33582  /*
33583  * - LGPL
33584  *
33585  * element
33586  * 
33587  */
33588
33589 /**
33590  * @class Roo.bootstrap.Brick
33591  * @extends Roo.bootstrap.Component
33592  * Bootstrap Brick class
33593  * 
33594  * @constructor
33595  * Create a new Brick
33596  * @param {Object} config The config object
33597  */
33598
33599 Roo.bootstrap.Brick = function(config){
33600     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33601     
33602     this.addEvents({
33603         // raw events
33604         /**
33605          * @event click
33606          * When a Brick is click
33607          * @param {Roo.bootstrap.Brick} this
33608          * @param {Roo.EventObject} e
33609          */
33610         "click" : true
33611     });
33612 };
33613
33614 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33615     
33616     /**
33617      * @cfg {String} title
33618      */   
33619     title : '',
33620     /**
33621      * @cfg {String} html
33622      */   
33623     html : '',
33624     /**
33625      * @cfg {String} bgimage
33626      */   
33627     bgimage : '',
33628     /**
33629      * @cfg {String} cls
33630      */   
33631     cls : '',
33632     /**
33633      * @cfg {String} href
33634      */   
33635     href : '',
33636     /**
33637      * @cfg {String} video
33638      */   
33639     video : '',
33640     /**
33641      * @cfg {Boolean} square
33642      */   
33643     square : true,
33644     
33645     getAutoCreate : function()
33646     {
33647         var cls = 'roo-brick';
33648         
33649         if(this.href.length){
33650             cls += ' roo-brick-link';
33651         }
33652         
33653         if(this.bgimage.length){
33654             cls += ' roo-brick-image';
33655         }
33656         
33657         if(!this.html.length && !this.bgimage.length){
33658             cls += ' roo-brick-center-title';
33659         }
33660         
33661         if(!this.html.length && this.bgimage.length){
33662             cls += ' roo-brick-bottom-title';
33663         }
33664         
33665         if(this.cls){
33666             cls += ' ' + this.cls;
33667         }
33668         
33669         var cfg = {
33670             tag: (this.href.length) ? 'a' : 'div',
33671             cls: cls,
33672             cn: [
33673                 {
33674                     tag: 'div',
33675                     cls: 'roo-brick-paragraph',
33676                     cn: []
33677                 }
33678             ]
33679         };
33680         
33681         if(this.href.length){
33682             cfg.href = this.href;
33683         }
33684         
33685         var cn = cfg.cn[0].cn;
33686         
33687         if(this.title.length){
33688             cn.push({
33689                 tag: 'h4',
33690                 cls: 'roo-brick-title',
33691                 html: this.title
33692             });
33693         }
33694         
33695         if(this.html.length){
33696             cn.push({
33697                 tag: 'p',
33698                 cls: 'roo-brick-text',
33699                 html: this.html
33700             });
33701         } else {
33702             cn.cls += ' hide';
33703         }
33704         
33705         if(this.bgimage.length){
33706             cfg.cn.push({
33707                 tag: 'img',
33708                 cls: 'roo-brick-image-view',
33709                 src: this.bgimage
33710             });
33711         }
33712         
33713         return cfg;
33714     },
33715     
33716     initEvents: function() 
33717     {
33718         if(this.title.length || this.html.length){
33719             this.el.on('mouseenter'  ,this.enter, this);
33720             this.el.on('mouseleave', this.leave, this);
33721         }
33722         
33723         Roo.EventManager.onWindowResize(this.resize, this); 
33724         
33725         if(this.bgimage.length){
33726             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33727             this.imageEl.on('load', this.onImageLoad, this);
33728             return;
33729         }
33730         
33731         this.resize();
33732     },
33733     
33734     onImageLoad : function()
33735     {
33736         this.resize();
33737     },
33738     
33739     resize : function()
33740     {
33741         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33742         
33743         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33744         
33745         if(this.bgimage.length){
33746             var image = this.el.select('.roo-brick-image-view', true).first();
33747             
33748             image.setWidth(paragraph.getWidth());
33749             
33750             if(this.square){
33751                 image.setHeight(paragraph.getWidth());
33752             }
33753             
33754             this.el.setHeight(image.getHeight());
33755             paragraph.setHeight(image.getHeight());
33756             
33757         }
33758         
33759     },
33760     
33761     enter: function(e, el)
33762     {
33763         e.preventDefault();
33764         
33765         if(this.bgimage.length){
33766             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33767             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33768         }
33769     },
33770     
33771     leave: function(e, el)
33772     {
33773         e.preventDefault();
33774         
33775         if(this.bgimage.length){
33776             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33777             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33778         }
33779     }
33780     
33781 });
33782
33783  
33784
33785  /*
33786  * - LGPL
33787  *
33788  * Number field 
33789  */
33790
33791 /**
33792  * @class Roo.bootstrap.NumberField
33793  * @extends Roo.bootstrap.Input
33794  * Bootstrap NumberField class
33795  * 
33796  * 
33797  * 
33798  * 
33799  * @constructor
33800  * Create a new NumberField
33801  * @param {Object} config The config object
33802  */
33803
33804 Roo.bootstrap.NumberField = function(config){
33805     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33806 };
33807
33808 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33809     
33810     /**
33811      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33812      */
33813     allowDecimals : true,
33814     /**
33815      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33816      */
33817     decimalSeparator : ".",
33818     /**
33819      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33820      */
33821     decimalPrecision : 2,
33822     /**
33823      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33824      */
33825     allowNegative : true,
33826     
33827     /**
33828      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33829      */
33830     allowZero: true,
33831     /**
33832      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33833      */
33834     minValue : Number.NEGATIVE_INFINITY,
33835     /**
33836      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33837      */
33838     maxValue : Number.MAX_VALUE,
33839     /**
33840      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33841      */
33842     minText : "The minimum value for this field is {0}",
33843     /**
33844      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33845      */
33846     maxText : "The maximum value for this field is {0}",
33847     /**
33848      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33849      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33850      */
33851     nanText : "{0} is not a valid number",
33852     /**
33853      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33854      */
33855     thousandsDelimiter : false,
33856     /**
33857      * @cfg {String} valueAlign alignment of value
33858      */
33859     valueAlign : "left",
33860
33861     getAutoCreate : function()
33862     {
33863         var hiddenInput = {
33864             tag: 'input',
33865             type: 'hidden',
33866             id: Roo.id(),
33867             cls: 'hidden-number-input'
33868         };
33869         
33870         if (this.name) {
33871             hiddenInput.name = this.name;
33872         }
33873         
33874         this.name = '';
33875         
33876         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33877         
33878         this.name = hiddenInput.name;
33879         
33880         if(cfg.cn.length > 0) {
33881             cfg.cn.push(hiddenInput);
33882         }
33883         
33884         return cfg;
33885     },
33886
33887     // private
33888     initEvents : function()
33889     {   
33890         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33891         
33892         var allowed = "0123456789";
33893         
33894         if(this.allowDecimals){
33895             allowed += this.decimalSeparator;
33896         }
33897         
33898         if(this.allowNegative){
33899             allowed += "-";
33900         }
33901         
33902         if(this.thousandsDelimiter) {
33903             allowed += ",";
33904         }
33905         
33906         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33907         
33908         var keyPress = function(e){
33909             
33910             var k = e.getKey();
33911             
33912             var c = e.getCharCode();
33913             
33914             if(
33915                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33916                     allowed.indexOf(String.fromCharCode(c)) === -1
33917             ){
33918                 e.stopEvent();
33919                 return;
33920             }
33921             
33922             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33923                 return;
33924             }
33925             
33926             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33927                 e.stopEvent();
33928             }
33929         };
33930         
33931         this.el.on("keypress", keyPress, this);
33932     },
33933     
33934     validateValue : function(value)
33935     {
33936         
33937         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33938             return false;
33939         }
33940         
33941         var num = this.parseValue(value);
33942         
33943         if(isNaN(num)){
33944             this.markInvalid(String.format(this.nanText, value));
33945             return false;
33946         }
33947         
33948         if(num < this.minValue){
33949             this.markInvalid(String.format(this.minText, this.minValue));
33950             return false;
33951         }
33952         
33953         if(num > this.maxValue){
33954             this.markInvalid(String.format(this.maxText, this.maxValue));
33955             return false;
33956         }
33957         
33958         return true;
33959     },
33960
33961     getValue : function()
33962     {
33963         var v = this.hiddenEl().getValue();
33964         
33965         return this.fixPrecision(this.parseValue(v));
33966     },
33967
33968     parseValue : function(value)
33969     {
33970         if(this.thousandsDelimiter) {
33971             value += "";
33972             r = new RegExp(",", "g");
33973             value = value.replace(r, "");
33974         }
33975         
33976         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33977         return isNaN(value) ? '' : value;
33978     },
33979
33980     fixPrecision : function(value)
33981     {
33982         if(this.thousandsDelimiter) {
33983             value += "";
33984             r = new RegExp(",", "g");
33985             value = value.replace(r, "");
33986         }
33987         
33988         var nan = isNaN(value);
33989         
33990         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33991             return nan ? '' : value;
33992         }
33993         return parseFloat(value).toFixed(this.decimalPrecision);
33994     },
33995
33996     setValue : function(v)
33997     {
33998         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33999         
34000         this.value = v;
34001         
34002         if(this.rendered){
34003             
34004             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
34005             
34006             this.inputEl().dom.value = (v == '') ? '' :
34007                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
34008             
34009             if(!this.allowZero && v === '0') {
34010                 this.hiddenEl().dom.value = '';
34011                 this.inputEl().dom.value = '';
34012             }
34013             
34014             this.validate();
34015         }
34016     },
34017
34018     decimalPrecisionFcn : function(v)
34019     {
34020         return Math.floor(v);
34021     },
34022
34023     beforeBlur : function()
34024     {
34025         var v = this.parseValue(this.getRawValue());
34026         
34027         if(v || v === 0 || v === ''){
34028             this.setValue(v);
34029         }
34030     },
34031     
34032     hiddenEl : function()
34033     {
34034         return this.el.select('input.hidden-number-input',true).first();
34035     }
34036     
34037 });
34038
34039  
34040
34041 /*
34042 * Licence: LGPL
34043 */
34044
34045 /**
34046  * @class Roo.bootstrap.DocumentSlider
34047  * @extends Roo.bootstrap.Component
34048  * Bootstrap DocumentSlider class
34049  * 
34050  * @constructor
34051  * Create a new DocumentViewer
34052  * @param {Object} config The config object
34053  */
34054
34055 Roo.bootstrap.DocumentSlider = function(config){
34056     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
34057     
34058     this.files = [];
34059     
34060     this.addEvents({
34061         /**
34062          * @event initial
34063          * Fire after initEvent
34064          * @param {Roo.bootstrap.DocumentSlider} this
34065          */
34066         "initial" : true,
34067         /**
34068          * @event update
34069          * Fire after update
34070          * @param {Roo.bootstrap.DocumentSlider} this
34071          */
34072         "update" : true,
34073         /**
34074          * @event click
34075          * Fire after click
34076          * @param {Roo.bootstrap.DocumentSlider} this
34077          */
34078         "click" : true
34079     });
34080 };
34081
34082 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
34083     
34084     files : false,
34085     
34086     indicator : 0,
34087     
34088     getAutoCreate : function()
34089     {
34090         var cfg = {
34091             tag : 'div',
34092             cls : 'roo-document-slider',
34093             cn : [
34094                 {
34095                     tag : 'div',
34096                     cls : 'roo-document-slider-header',
34097                     cn : [
34098                         {
34099                             tag : 'div',
34100                             cls : 'roo-document-slider-header-title'
34101                         }
34102                     ]
34103                 },
34104                 {
34105                     tag : 'div',
34106                     cls : 'roo-document-slider-body',
34107                     cn : [
34108                         {
34109                             tag : 'div',
34110                             cls : 'roo-document-slider-prev',
34111                             cn : [
34112                                 {
34113                                     tag : 'i',
34114                                     cls : 'fa fa-chevron-left'
34115                                 }
34116                             ]
34117                         },
34118                         {
34119                             tag : 'div',
34120                             cls : 'roo-document-slider-thumb',
34121                             cn : [
34122                                 {
34123                                     tag : 'img',
34124                                     cls : 'roo-document-slider-image'
34125                                 }
34126                             ]
34127                         },
34128                         {
34129                             tag : 'div',
34130                             cls : 'roo-document-slider-next',
34131                             cn : [
34132                                 {
34133                                     tag : 'i',
34134                                     cls : 'fa fa-chevron-right'
34135                                 }
34136                             ]
34137                         }
34138                     ]
34139                 }
34140             ]
34141         };
34142         
34143         return cfg;
34144     },
34145     
34146     initEvents : function()
34147     {
34148         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34149         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34150         
34151         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34152         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34153         
34154         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34155         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34156         
34157         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34158         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34159         
34160         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34161         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34162         
34163         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34164         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34165         
34166         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34167         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34168         
34169         this.thumbEl.on('click', this.onClick, this);
34170         
34171         this.prevIndicator.on('click', this.prev, this);
34172         
34173         this.nextIndicator.on('click', this.next, this);
34174         
34175     },
34176     
34177     initial : function()
34178     {
34179         if(this.files.length){
34180             this.indicator = 1;
34181             this.update()
34182         }
34183         
34184         this.fireEvent('initial', this);
34185     },
34186     
34187     update : function()
34188     {
34189         this.imageEl.attr('src', this.files[this.indicator - 1]);
34190         
34191         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34192         
34193         this.prevIndicator.show();
34194         
34195         if(this.indicator == 1){
34196             this.prevIndicator.hide();
34197         }
34198         
34199         this.nextIndicator.show();
34200         
34201         if(this.indicator == this.files.length){
34202             this.nextIndicator.hide();
34203         }
34204         
34205         this.thumbEl.scrollTo('top');
34206         
34207         this.fireEvent('update', this);
34208     },
34209     
34210     onClick : function(e)
34211     {
34212         e.preventDefault();
34213         
34214         this.fireEvent('click', this);
34215     },
34216     
34217     prev : function(e)
34218     {
34219         e.preventDefault();
34220         
34221         this.indicator = Math.max(1, this.indicator - 1);
34222         
34223         this.update();
34224     },
34225     
34226     next : function(e)
34227     {
34228         e.preventDefault();
34229         
34230         this.indicator = Math.min(this.files.length, this.indicator + 1);
34231         
34232         this.update();
34233     }
34234 });
34235 /*
34236  * - LGPL
34237  *
34238  * RadioSet
34239  *
34240  *
34241  */
34242
34243 /**
34244  * @class Roo.bootstrap.RadioSet
34245  * @extends Roo.bootstrap.Input
34246  * Bootstrap RadioSet class
34247  * @cfg {String} indicatorpos (left|right) default left
34248  * @cfg {Boolean} inline (true|false) inline the element (default true)
34249  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34250  * @constructor
34251  * Create a new RadioSet
34252  * @param {Object} config The config object
34253  */
34254
34255 Roo.bootstrap.RadioSet = function(config){
34256     
34257     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34258     
34259     this.radioes = [];
34260     
34261     Roo.bootstrap.RadioSet.register(this);
34262     
34263     this.addEvents({
34264         /**
34265         * @event check
34266         * Fires when the element is checked or unchecked.
34267         * @param {Roo.bootstrap.RadioSet} this This radio
34268         * @param {Roo.bootstrap.Radio} item The checked item
34269         */
34270        check : true,
34271        /**
34272         * @event click
34273         * Fires when the element is click.
34274         * @param {Roo.bootstrap.RadioSet} this This radio set
34275         * @param {Roo.bootstrap.Radio} item The checked item
34276         * @param {Roo.EventObject} e The event object
34277         */
34278        click : true
34279     });
34280     
34281 };
34282
34283 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34284
34285     radioes : false,
34286     
34287     inline : true,
34288     
34289     weight : '',
34290     
34291     indicatorpos : 'left',
34292     
34293     getAutoCreate : function()
34294     {
34295         var label = {
34296             tag : 'label',
34297             cls : 'roo-radio-set-label',
34298             cn : [
34299                 {
34300                     tag : 'span',
34301                     html : this.fieldLabel
34302                 }
34303             ]
34304         };
34305         if (Roo.bootstrap.version == 3) {
34306             
34307             
34308             if(this.indicatorpos == 'left'){
34309                 label.cn.unshift({
34310                     tag : 'i',
34311                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34312                     tooltip : 'This field is required'
34313                 });
34314             } else {
34315                 label.cn.push({
34316                     tag : 'i',
34317                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34318                     tooltip : 'This field is required'
34319                 });
34320             }
34321         }
34322         var items = {
34323             tag : 'div',
34324             cls : 'roo-radio-set-items'
34325         };
34326         
34327         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34328         
34329         if (align === 'left' && this.fieldLabel.length) {
34330             
34331             items = {
34332                 cls : "roo-radio-set-right", 
34333                 cn: [
34334                     items
34335                 ]
34336             };
34337             
34338             if(this.labelWidth > 12){
34339                 label.style = "width: " + this.labelWidth + 'px';
34340             }
34341             
34342             if(this.labelWidth < 13 && this.labelmd == 0){
34343                 this.labelmd = this.labelWidth;
34344             }
34345             
34346             if(this.labellg > 0){
34347                 label.cls += ' col-lg-' + this.labellg;
34348                 items.cls += ' col-lg-' + (12 - this.labellg);
34349             }
34350             
34351             if(this.labelmd > 0){
34352                 label.cls += ' col-md-' + this.labelmd;
34353                 items.cls += ' col-md-' + (12 - this.labelmd);
34354             }
34355             
34356             if(this.labelsm > 0){
34357                 label.cls += ' col-sm-' + this.labelsm;
34358                 items.cls += ' col-sm-' + (12 - this.labelsm);
34359             }
34360             
34361             if(this.labelxs > 0){
34362                 label.cls += ' col-xs-' + this.labelxs;
34363                 items.cls += ' col-xs-' + (12 - this.labelxs);
34364             }
34365         }
34366         
34367         var cfg = {
34368             tag : 'div',
34369             cls : 'roo-radio-set',
34370             cn : [
34371                 {
34372                     tag : 'input',
34373                     cls : 'roo-radio-set-input',
34374                     type : 'hidden',
34375                     name : this.name,
34376                     value : this.value ? this.value :  ''
34377                 },
34378                 label,
34379                 items
34380             ]
34381         };
34382         
34383         if(this.weight.length){
34384             cfg.cls += ' roo-radio-' + this.weight;
34385         }
34386         
34387         if(this.inline) {
34388             cfg.cls += ' roo-radio-set-inline';
34389         }
34390         
34391         var settings=this;
34392         ['xs','sm','md','lg'].map(function(size){
34393             if (settings[size]) {
34394                 cfg.cls += ' col-' + size + '-' + settings[size];
34395             }
34396         });
34397         
34398         return cfg;
34399         
34400     },
34401
34402     initEvents : function()
34403     {
34404         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34405         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34406         
34407         if(!this.fieldLabel.length){
34408             this.labelEl.hide();
34409         }
34410         
34411         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34412         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34413         
34414         this.indicator = this.indicatorEl();
34415         
34416         if(this.indicator){
34417             this.indicator.addClass('invisible');
34418         }
34419         
34420         this.originalValue = this.getValue();
34421         
34422     },
34423     
34424     inputEl: function ()
34425     {
34426         return this.el.select('.roo-radio-set-input', true).first();
34427     },
34428     
34429     getChildContainer : function()
34430     {
34431         return this.itemsEl;
34432     },
34433     
34434     register : function(item)
34435     {
34436         this.radioes.push(item);
34437         
34438     },
34439     
34440     validate : function()
34441     {   
34442         if(this.getVisibilityEl().hasClass('hidden')){
34443             return true;
34444         }
34445         
34446         var valid = false;
34447         
34448         Roo.each(this.radioes, function(i){
34449             if(!i.checked){
34450                 return;
34451             }
34452             
34453             valid = true;
34454             return false;
34455         });
34456         
34457         if(this.allowBlank) {
34458             return true;
34459         }
34460         
34461         if(this.disabled || valid){
34462             this.markValid();
34463             return true;
34464         }
34465         
34466         this.markInvalid();
34467         return false;
34468         
34469     },
34470     
34471     markValid : function()
34472     {
34473         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34474             this.indicatorEl().removeClass('visible');
34475             this.indicatorEl().addClass('invisible');
34476         }
34477         
34478         
34479         if (Roo.bootstrap.version == 3) {
34480             this.el.removeClass([this.invalidClass, this.validClass]);
34481             this.el.addClass(this.validClass);
34482         } else {
34483             this.el.removeClass(['is-invalid','is-valid']);
34484             this.el.addClass(['is-valid']);
34485         }
34486         this.fireEvent('valid', this);
34487     },
34488     
34489     markInvalid : function(msg)
34490     {
34491         if(this.allowBlank || this.disabled){
34492             return;
34493         }
34494         
34495         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34496             this.indicatorEl().removeClass('invisible');
34497             this.indicatorEl().addClass('visible');
34498         }
34499         if (Roo.bootstrap.version == 3) {
34500             this.el.removeClass([this.invalidClass, this.validClass]);
34501             this.el.addClass(this.invalidClass);
34502         } else {
34503             this.el.removeClass(['is-invalid','is-valid']);
34504             this.el.addClass(['is-invalid']);
34505         }
34506         
34507         this.fireEvent('invalid', this, msg);
34508         
34509     },
34510     
34511     setValue : function(v, suppressEvent)
34512     {   
34513         if(this.value === v){
34514             return;
34515         }
34516         
34517         this.value = v;
34518         
34519         if(this.rendered){
34520             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34521         }
34522         
34523         Roo.each(this.radioes, function(i){
34524             i.checked = false;
34525             i.el.removeClass('checked');
34526         });
34527         
34528         Roo.each(this.radioes, function(i){
34529             
34530             if(i.value === v || i.value.toString() === v.toString()){
34531                 i.checked = true;
34532                 i.el.addClass('checked');
34533                 
34534                 if(suppressEvent !== true){
34535                     this.fireEvent('check', this, i);
34536                 }
34537                 
34538                 return false;
34539             }
34540             
34541         }, this);
34542         
34543         this.validate();
34544     },
34545     
34546     clearInvalid : function(){
34547         
34548         if(!this.el || this.preventMark){
34549             return;
34550         }
34551         
34552         this.el.removeClass([this.invalidClass]);
34553         
34554         this.fireEvent('valid', this);
34555     }
34556     
34557 });
34558
34559 Roo.apply(Roo.bootstrap.RadioSet, {
34560     
34561     groups: {},
34562     
34563     register : function(set)
34564     {
34565         this.groups[set.name] = set;
34566     },
34567     
34568     get: function(name) 
34569     {
34570         if (typeof(this.groups[name]) == 'undefined') {
34571             return false;
34572         }
34573         
34574         return this.groups[name] ;
34575     }
34576     
34577 });
34578 /*
34579  * Based on:
34580  * Ext JS Library 1.1.1
34581  * Copyright(c) 2006-2007, Ext JS, LLC.
34582  *
34583  * Originally Released Under LGPL - original licence link has changed is not relivant.
34584  *
34585  * Fork - LGPL
34586  * <script type="text/javascript">
34587  */
34588
34589
34590 /**
34591  * @class Roo.bootstrap.SplitBar
34592  * @extends Roo.util.Observable
34593  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34594  * <br><br>
34595  * Usage:
34596  * <pre><code>
34597 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34598                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34599 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34600 split.minSize = 100;
34601 split.maxSize = 600;
34602 split.animate = true;
34603 split.on('moved', splitterMoved);
34604 </code></pre>
34605  * @constructor
34606  * Create a new SplitBar
34607  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34608  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34609  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34610  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34611                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34612                         position of the SplitBar).
34613  */
34614 Roo.bootstrap.SplitBar = function(cfg){
34615     
34616     /** @private */
34617     
34618     //{
34619     //  dragElement : elm
34620     //  resizingElement: el,
34621         // optional..
34622     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34623     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34624         // existingProxy ???
34625     //}
34626     
34627     this.el = Roo.get(cfg.dragElement, true);
34628     this.el.dom.unselectable = "on";
34629     /** @private */
34630     this.resizingEl = Roo.get(cfg.resizingElement, true);
34631
34632     /**
34633      * @private
34634      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34635      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34636      * @type Number
34637      */
34638     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34639     
34640     /**
34641      * The minimum size of the resizing element. (Defaults to 0)
34642      * @type Number
34643      */
34644     this.minSize = 0;
34645     
34646     /**
34647      * The maximum size of the resizing element. (Defaults to 2000)
34648      * @type Number
34649      */
34650     this.maxSize = 2000;
34651     
34652     /**
34653      * Whether to animate the transition to the new size
34654      * @type Boolean
34655      */
34656     this.animate = false;
34657     
34658     /**
34659      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34660      * @type Boolean
34661      */
34662     this.useShim = false;
34663     
34664     /** @private */
34665     this.shim = null;
34666     
34667     if(!cfg.existingProxy){
34668         /** @private */
34669         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34670     }else{
34671         this.proxy = Roo.get(cfg.existingProxy).dom;
34672     }
34673     /** @private */
34674     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34675     
34676     /** @private */
34677     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34678     
34679     /** @private */
34680     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34681     
34682     /** @private */
34683     this.dragSpecs = {};
34684     
34685     /**
34686      * @private The adapter to use to positon and resize elements
34687      */
34688     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34689     this.adapter.init(this);
34690     
34691     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34692         /** @private */
34693         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34694         this.el.addClass("roo-splitbar-h");
34695     }else{
34696         /** @private */
34697         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34698         this.el.addClass("roo-splitbar-v");
34699     }
34700     
34701     this.addEvents({
34702         /**
34703          * @event resize
34704          * Fires when the splitter is moved (alias for {@link #event-moved})
34705          * @param {Roo.bootstrap.SplitBar} this
34706          * @param {Number} newSize the new width or height
34707          */
34708         "resize" : true,
34709         /**
34710          * @event moved
34711          * Fires when the splitter is moved
34712          * @param {Roo.bootstrap.SplitBar} this
34713          * @param {Number} newSize the new width or height
34714          */
34715         "moved" : true,
34716         /**
34717          * @event beforeresize
34718          * Fires before the splitter is dragged
34719          * @param {Roo.bootstrap.SplitBar} this
34720          */
34721         "beforeresize" : true,
34722
34723         "beforeapply" : true
34724     });
34725
34726     Roo.util.Observable.call(this);
34727 };
34728
34729 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34730     onStartProxyDrag : function(x, y){
34731         this.fireEvent("beforeresize", this);
34732         if(!this.overlay){
34733             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34734             o.unselectable();
34735             o.enableDisplayMode("block");
34736             // all splitbars share the same overlay
34737             Roo.bootstrap.SplitBar.prototype.overlay = o;
34738         }
34739         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34740         this.overlay.show();
34741         Roo.get(this.proxy).setDisplayed("block");
34742         var size = this.adapter.getElementSize(this);
34743         this.activeMinSize = this.getMinimumSize();;
34744         this.activeMaxSize = this.getMaximumSize();;
34745         var c1 = size - this.activeMinSize;
34746         var c2 = Math.max(this.activeMaxSize - size, 0);
34747         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34748             this.dd.resetConstraints();
34749             this.dd.setXConstraint(
34750                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34751                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34752             );
34753             this.dd.setYConstraint(0, 0);
34754         }else{
34755             this.dd.resetConstraints();
34756             this.dd.setXConstraint(0, 0);
34757             this.dd.setYConstraint(
34758                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34759                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34760             );
34761          }
34762         this.dragSpecs.startSize = size;
34763         this.dragSpecs.startPoint = [x, y];
34764         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34765     },
34766     
34767     /** 
34768      * @private Called after the drag operation by the DDProxy
34769      */
34770     onEndProxyDrag : function(e){
34771         Roo.get(this.proxy).setDisplayed(false);
34772         var endPoint = Roo.lib.Event.getXY(e);
34773         if(this.overlay){
34774             this.overlay.hide();
34775         }
34776         var newSize;
34777         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34778             newSize = this.dragSpecs.startSize + 
34779                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34780                     endPoint[0] - this.dragSpecs.startPoint[0] :
34781                     this.dragSpecs.startPoint[0] - endPoint[0]
34782                 );
34783         }else{
34784             newSize = this.dragSpecs.startSize + 
34785                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34786                     endPoint[1] - this.dragSpecs.startPoint[1] :
34787                     this.dragSpecs.startPoint[1] - endPoint[1]
34788                 );
34789         }
34790         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34791         if(newSize != this.dragSpecs.startSize){
34792             if(this.fireEvent('beforeapply', this, newSize) !== false){
34793                 this.adapter.setElementSize(this, newSize);
34794                 this.fireEvent("moved", this, newSize);
34795                 this.fireEvent("resize", this, newSize);
34796             }
34797         }
34798     },
34799     
34800     /**
34801      * Get the adapter this SplitBar uses
34802      * @return The adapter object
34803      */
34804     getAdapter : function(){
34805         return this.adapter;
34806     },
34807     
34808     /**
34809      * Set the adapter this SplitBar uses
34810      * @param {Object} adapter A SplitBar adapter object
34811      */
34812     setAdapter : function(adapter){
34813         this.adapter = adapter;
34814         this.adapter.init(this);
34815     },
34816     
34817     /**
34818      * Gets the minimum size for the resizing element
34819      * @return {Number} The minimum size
34820      */
34821     getMinimumSize : function(){
34822         return this.minSize;
34823     },
34824     
34825     /**
34826      * Sets the minimum size for the resizing element
34827      * @param {Number} minSize The minimum size
34828      */
34829     setMinimumSize : function(minSize){
34830         this.minSize = minSize;
34831     },
34832     
34833     /**
34834      * Gets the maximum size for the resizing element
34835      * @return {Number} The maximum size
34836      */
34837     getMaximumSize : function(){
34838         return this.maxSize;
34839     },
34840     
34841     /**
34842      * Sets the maximum size for the resizing element
34843      * @param {Number} maxSize The maximum size
34844      */
34845     setMaximumSize : function(maxSize){
34846         this.maxSize = maxSize;
34847     },
34848     
34849     /**
34850      * Sets the initialize size for the resizing element
34851      * @param {Number} size The initial size
34852      */
34853     setCurrentSize : function(size){
34854         var oldAnimate = this.animate;
34855         this.animate = false;
34856         this.adapter.setElementSize(this, size);
34857         this.animate = oldAnimate;
34858     },
34859     
34860     /**
34861      * Destroy this splitbar. 
34862      * @param {Boolean} removeEl True to remove the element
34863      */
34864     destroy : function(removeEl){
34865         if(this.shim){
34866             this.shim.remove();
34867         }
34868         this.dd.unreg();
34869         this.proxy.parentNode.removeChild(this.proxy);
34870         if(removeEl){
34871             this.el.remove();
34872         }
34873     }
34874 });
34875
34876 /**
34877  * @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.
34878  */
34879 Roo.bootstrap.SplitBar.createProxy = function(dir){
34880     var proxy = new Roo.Element(document.createElement("div"));
34881     proxy.unselectable();
34882     var cls = 'roo-splitbar-proxy';
34883     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34884     document.body.appendChild(proxy.dom);
34885     return proxy.dom;
34886 };
34887
34888 /** 
34889  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34890  * Default Adapter. It assumes the splitter and resizing element are not positioned
34891  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34892  */
34893 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34894 };
34895
34896 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34897     // do nothing for now
34898     init : function(s){
34899     
34900     },
34901     /**
34902      * Called before drag operations to get the current size of the resizing element. 
34903      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34904      */
34905      getElementSize : function(s){
34906         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34907             return s.resizingEl.getWidth();
34908         }else{
34909             return s.resizingEl.getHeight();
34910         }
34911     },
34912     
34913     /**
34914      * Called after drag operations to set the size of the resizing element.
34915      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34916      * @param {Number} newSize The new size to set
34917      * @param {Function} onComplete A function to be invoked when resizing is complete
34918      */
34919     setElementSize : function(s, newSize, onComplete){
34920         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34921             if(!s.animate){
34922                 s.resizingEl.setWidth(newSize);
34923                 if(onComplete){
34924                     onComplete(s, newSize);
34925                 }
34926             }else{
34927                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34928             }
34929         }else{
34930             
34931             if(!s.animate){
34932                 s.resizingEl.setHeight(newSize);
34933                 if(onComplete){
34934                     onComplete(s, newSize);
34935                 }
34936             }else{
34937                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34938             }
34939         }
34940     }
34941 };
34942
34943 /** 
34944  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34945  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34946  * Adapter that  moves the splitter element to align with the resized sizing element. 
34947  * Used with an absolute positioned SplitBar.
34948  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34949  * document.body, make sure you assign an id to the body element.
34950  */
34951 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34952     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34953     this.container = Roo.get(container);
34954 };
34955
34956 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34957     init : function(s){
34958         this.basic.init(s);
34959     },
34960     
34961     getElementSize : function(s){
34962         return this.basic.getElementSize(s);
34963     },
34964     
34965     setElementSize : function(s, newSize, onComplete){
34966         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34967     },
34968     
34969     moveSplitter : function(s){
34970         var yes = Roo.bootstrap.SplitBar;
34971         switch(s.placement){
34972             case yes.LEFT:
34973                 s.el.setX(s.resizingEl.getRight());
34974                 break;
34975             case yes.RIGHT:
34976                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34977                 break;
34978             case yes.TOP:
34979                 s.el.setY(s.resizingEl.getBottom());
34980                 break;
34981             case yes.BOTTOM:
34982                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34983                 break;
34984         }
34985     }
34986 };
34987
34988 /**
34989  * Orientation constant - Create a vertical SplitBar
34990  * @static
34991  * @type Number
34992  */
34993 Roo.bootstrap.SplitBar.VERTICAL = 1;
34994
34995 /**
34996  * Orientation constant - Create a horizontal SplitBar
34997  * @static
34998  * @type Number
34999  */
35000 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
35001
35002 /**
35003  * Placement constant - The resizing element is to the left of the splitter element
35004  * @static
35005  * @type Number
35006  */
35007 Roo.bootstrap.SplitBar.LEFT = 1;
35008
35009 /**
35010  * Placement constant - The resizing element is to the right of the splitter element
35011  * @static
35012  * @type Number
35013  */
35014 Roo.bootstrap.SplitBar.RIGHT = 2;
35015
35016 /**
35017  * Placement constant - The resizing element is positioned above the splitter element
35018  * @static
35019  * @type Number
35020  */
35021 Roo.bootstrap.SplitBar.TOP = 3;
35022
35023 /**
35024  * Placement constant - The resizing element is positioned under splitter element
35025  * @static
35026  * @type Number
35027  */
35028 Roo.bootstrap.SplitBar.BOTTOM = 4;
35029 Roo.namespace("Roo.bootstrap.layout");/*
35030  * Based on:
35031  * Ext JS Library 1.1.1
35032  * Copyright(c) 2006-2007, Ext JS, LLC.
35033  *
35034  * Originally Released Under LGPL - original licence link has changed is not relivant.
35035  *
35036  * Fork - LGPL
35037  * <script type="text/javascript">
35038  */
35039
35040 /**
35041  * @class Roo.bootstrap.layout.Manager
35042  * @extends Roo.bootstrap.Component
35043  * Base class for layout managers.
35044  */
35045 Roo.bootstrap.layout.Manager = function(config)
35046 {
35047     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
35048
35049
35050
35051
35052
35053     /** false to disable window resize monitoring @type Boolean */
35054     this.monitorWindowResize = true;
35055     this.regions = {};
35056     this.addEvents({
35057         /**
35058          * @event layout
35059          * Fires when a layout is performed.
35060          * @param {Roo.LayoutManager} this
35061          */
35062         "layout" : true,
35063         /**
35064          * @event regionresized
35065          * Fires when the user resizes a region.
35066          * @param {Roo.LayoutRegion} region The resized region
35067          * @param {Number} newSize The new size (width for east/west, height for north/south)
35068          */
35069         "regionresized" : true,
35070         /**
35071          * @event regioncollapsed
35072          * Fires when a region is collapsed.
35073          * @param {Roo.LayoutRegion} region The collapsed region
35074          */
35075         "regioncollapsed" : true,
35076         /**
35077          * @event regionexpanded
35078          * Fires when a region is expanded.
35079          * @param {Roo.LayoutRegion} region The expanded region
35080          */
35081         "regionexpanded" : true
35082     });
35083     this.updating = false;
35084
35085     if (config.el) {
35086         this.el = Roo.get(config.el);
35087         this.initEvents();
35088     }
35089
35090 };
35091
35092 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35093
35094
35095     regions : null,
35096
35097     monitorWindowResize : true,
35098
35099
35100     updating : false,
35101
35102
35103     onRender : function(ct, position)
35104     {
35105         if(!this.el){
35106             this.el = Roo.get(ct);
35107             this.initEvents();
35108         }
35109         //this.fireEvent('render',this);
35110     },
35111
35112
35113     initEvents: function()
35114     {
35115
35116
35117         // ie scrollbar fix
35118         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35119             document.body.scroll = "no";
35120         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35121             this.el.position('relative');
35122         }
35123         this.id = this.el.id;
35124         this.el.addClass("roo-layout-container");
35125         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35126         if(this.el.dom != document.body ) {
35127             this.el.on('resize', this.layout,this);
35128             this.el.on('show', this.layout,this);
35129         }
35130
35131     },
35132
35133     /**
35134      * Returns true if this layout is currently being updated
35135      * @return {Boolean}
35136      */
35137     isUpdating : function(){
35138         return this.updating;
35139     },
35140
35141     /**
35142      * Suspend the LayoutManager from doing auto-layouts while
35143      * making multiple add or remove calls
35144      */
35145     beginUpdate : function(){
35146         this.updating = true;
35147     },
35148
35149     /**
35150      * Restore auto-layouts and optionally disable the manager from performing a layout
35151      * @param {Boolean} noLayout true to disable a layout update
35152      */
35153     endUpdate : function(noLayout){
35154         this.updating = false;
35155         if(!noLayout){
35156             this.layout();
35157         }
35158     },
35159
35160     layout: function(){
35161         // abstract...
35162     },
35163
35164     onRegionResized : function(region, newSize){
35165         this.fireEvent("regionresized", region, newSize);
35166         this.layout();
35167     },
35168
35169     onRegionCollapsed : function(region){
35170         this.fireEvent("regioncollapsed", region);
35171     },
35172
35173     onRegionExpanded : function(region){
35174         this.fireEvent("regionexpanded", region);
35175     },
35176
35177     /**
35178      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35179      * performs box-model adjustments.
35180      * @return {Object} The size as an object {width: (the width), height: (the height)}
35181      */
35182     getViewSize : function()
35183     {
35184         var size;
35185         if(this.el.dom != document.body){
35186             size = this.el.getSize();
35187         }else{
35188             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35189         }
35190         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35191         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35192         return size;
35193     },
35194
35195     /**
35196      * Returns the Element this layout is bound to.
35197      * @return {Roo.Element}
35198      */
35199     getEl : function(){
35200         return this.el;
35201     },
35202
35203     /**
35204      * Returns the specified region.
35205      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35206      * @return {Roo.LayoutRegion}
35207      */
35208     getRegion : function(target){
35209         return this.regions[target.toLowerCase()];
35210     },
35211
35212     onWindowResize : function(){
35213         if(this.monitorWindowResize){
35214             this.layout();
35215         }
35216     }
35217 });
35218 /*
35219  * Based on:
35220  * Ext JS Library 1.1.1
35221  * Copyright(c) 2006-2007, Ext JS, LLC.
35222  *
35223  * Originally Released Under LGPL - original licence link has changed is not relivant.
35224  *
35225  * Fork - LGPL
35226  * <script type="text/javascript">
35227  */
35228 /**
35229  * @class Roo.bootstrap.layout.Border
35230  * @extends Roo.bootstrap.layout.Manager
35231  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35232  * please see: examples/bootstrap/nested.html<br><br>
35233  
35234 <b>The container the layout is rendered into can be either the body element or any other element.
35235 If it is not the body element, the container needs to either be an absolute positioned element,
35236 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35237 the container size if it is not the body element.</b>
35238
35239 * @constructor
35240 * Create a new Border
35241 * @param {Object} config Configuration options
35242  */
35243 Roo.bootstrap.layout.Border = function(config){
35244     config = config || {};
35245     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35246     
35247     
35248     
35249     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35250         if(config[region]){
35251             config[region].region = region;
35252             this.addRegion(config[region]);
35253         }
35254     },this);
35255     
35256 };
35257
35258 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35259
35260 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35261     
35262     parent : false, // this might point to a 'nest' or a ???
35263     
35264     /**
35265      * Creates and adds a new region if it doesn't already exist.
35266      * @param {String} target The target region key (north, south, east, west or center).
35267      * @param {Object} config The regions config object
35268      * @return {BorderLayoutRegion} The new region
35269      */
35270     addRegion : function(config)
35271     {
35272         if(!this.regions[config.region]){
35273             var r = this.factory(config);
35274             this.bindRegion(r);
35275         }
35276         return this.regions[config.region];
35277     },
35278
35279     // private (kinda)
35280     bindRegion : function(r){
35281         this.regions[r.config.region] = r;
35282         
35283         r.on("visibilitychange",    this.layout, this);
35284         r.on("paneladded",          this.layout, this);
35285         r.on("panelremoved",        this.layout, this);
35286         r.on("invalidated",         this.layout, this);
35287         r.on("resized",             this.onRegionResized, this);
35288         r.on("collapsed",           this.onRegionCollapsed, this);
35289         r.on("expanded",            this.onRegionExpanded, this);
35290     },
35291
35292     /**
35293      * Performs a layout update.
35294      */
35295     layout : function()
35296     {
35297         if(this.updating) {
35298             return;
35299         }
35300         
35301         // render all the rebions if they have not been done alreayd?
35302         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35303             if(this.regions[region] && !this.regions[region].bodyEl){
35304                 this.regions[region].onRender(this.el)
35305             }
35306         },this);
35307         
35308         var size = this.getViewSize();
35309         var w = size.width;
35310         var h = size.height;
35311         var centerW = w;
35312         var centerH = h;
35313         var centerY = 0;
35314         var centerX = 0;
35315         //var x = 0, y = 0;
35316
35317         var rs = this.regions;
35318         var north = rs["north"];
35319         var south = rs["south"]; 
35320         var west = rs["west"];
35321         var east = rs["east"];
35322         var center = rs["center"];
35323         //if(this.hideOnLayout){ // not supported anymore
35324             //c.el.setStyle("display", "none");
35325         //}
35326         if(north && north.isVisible()){
35327             var b = north.getBox();
35328             var m = north.getMargins();
35329             b.width = w - (m.left+m.right);
35330             b.x = m.left;
35331             b.y = m.top;
35332             centerY = b.height + b.y + m.bottom;
35333             centerH -= centerY;
35334             north.updateBox(this.safeBox(b));
35335         }
35336         if(south && south.isVisible()){
35337             var b = south.getBox();
35338             var m = south.getMargins();
35339             b.width = w - (m.left+m.right);
35340             b.x = m.left;
35341             var totalHeight = (b.height + m.top + m.bottom);
35342             b.y = h - totalHeight + m.top;
35343             centerH -= totalHeight;
35344             south.updateBox(this.safeBox(b));
35345         }
35346         if(west && west.isVisible()){
35347             var b = west.getBox();
35348             var m = west.getMargins();
35349             b.height = centerH - (m.top+m.bottom);
35350             b.x = m.left;
35351             b.y = centerY + m.top;
35352             var totalWidth = (b.width + m.left + m.right);
35353             centerX += totalWidth;
35354             centerW -= totalWidth;
35355             west.updateBox(this.safeBox(b));
35356         }
35357         if(east && east.isVisible()){
35358             var b = east.getBox();
35359             var m = east.getMargins();
35360             b.height = centerH - (m.top+m.bottom);
35361             var totalWidth = (b.width + m.left + m.right);
35362             b.x = w - totalWidth + m.left;
35363             b.y = centerY + m.top;
35364             centerW -= totalWidth;
35365             east.updateBox(this.safeBox(b));
35366         }
35367         if(center){
35368             var m = center.getMargins();
35369             var centerBox = {
35370                 x: centerX + m.left,
35371                 y: centerY + m.top,
35372                 width: centerW - (m.left+m.right),
35373                 height: centerH - (m.top+m.bottom)
35374             };
35375             //if(this.hideOnLayout){
35376                 //center.el.setStyle("display", "block");
35377             //}
35378             center.updateBox(this.safeBox(centerBox));
35379         }
35380         this.el.repaint();
35381         this.fireEvent("layout", this);
35382     },
35383
35384     // private
35385     safeBox : function(box){
35386         box.width = Math.max(0, box.width);
35387         box.height = Math.max(0, box.height);
35388         return box;
35389     },
35390
35391     /**
35392      * Adds a ContentPanel (or subclass) to this layout.
35393      * @param {String} target The target region key (north, south, east, west or center).
35394      * @param {Roo.ContentPanel} panel The panel to add
35395      * @return {Roo.ContentPanel} The added panel
35396      */
35397     add : function(target, panel){
35398          
35399         target = target.toLowerCase();
35400         return this.regions[target].add(panel);
35401     },
35402
35403     /**
35404      * Remove a ContentPanel (or subclass) to this layout.
35405      * @param {String} target The target region key (north, south, east, west or center).
35406      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35407      * @return {Roo.ContentPanel} The removed panel
35408      */
35409     remove : function(target, panel){
35410         target = target.toLowerCase();
35411         return this.regions[target].remove(panel);
35412     },
35413
35414     /**
35415      * Searches all regions for a panel with the specified id
35416      * @param {String} panelId
35417      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35418      */
35419     findPanel : function(panelId){
35420         var rs = this.regions;
35421         for(var target in rs){
35422             if(typeof rs[target] != "function"){
35423                 var p = rs[target].getPanel(panelId);
35424                 if(p){
35425                     return p;
35426                 }
35427             }
35428         }
35429         return null;
35430     },
35431
35432     /**
35433      * Searches all regions for a panel with the specified id and activates (shows) it.
35434      * @param {String/ContentPanel} panelId The panels id or the panel itself
35435      * @return {Roo.ContentPanel} The shown panel or null
35436      */
35437     showPanel : function(panelId) {
35438       var rs = this.regions;
35439       for(var target in rs){
35440          var r = rs[target];
35441          if(typeof r != "function"){
35442             if(r.hasPanel(panelId)){
35443                return r.showPanel(panelId);
35444             }
35445          }
35446       }
35447       return null;
35448    },
35449
35450    /**
35451      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35452      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35453      */
35454    /*
35455     restoreState : function(provider){
35456         if(!provider){
35457             provider = Roo.state.Manager;
35458         }
35459         var sm = new Roo.LayoutStateManager();
35460         sm.init(this, provider);
35461     },
35462 */
35463  
35464  
35465     /**
35466      * Adds a xtype elements to the layout.
35467      * <pre><code>
35468
35469 layout.addxtype({
35470        xtype : 'ContentPanel',
35471        region: 'west',
35472        items: [ .... ]
35473    }
35474 );
35475
35476 layout.addxtype({
35477         xtype : 'NestedLayoutPanel',
35478         region: 'west',
35479         layout: {
35480            center: { },
35481            west: { }   
35482         },
35483         items : [ ... list of content panels or nested layout panels.. ]
35484    }
35485 );
35486 </code></pre>
35487      * @param {Object} cfg Xtype definition of item to add.
35488      */
35489     addxtype : function(cfg)
35490     {
35491         // basically accepts a pannel...
35492         // can accept a layout region..!?!?
35493         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35494         
35495         
35496         // theory?  children can only be panels??
35497         
35498         //if (!cfg.xtype.match(/Panel$/)) {
35499         //    return false;
35500         //}
35501         var ret = false;
35502         
35503         if (typeof(cfg.region) == 'undefined') {
35504             Roo.log("Failed to add Panel, region was not set");
35505             Roo.log(cfg);
35506             return false;
35507         }
35508         var region = cfg.region;
35509         delete cfg.region;
35510         
35511           
35512         var xitems = [];
35513         if (cfg.items) {
35514             xitems = cfg.items;
35515             delete cfg.items;
35516         }
35517         var nb = false;
35518         
35519         if ( region == 'center') {
35520             Roo.log("Center: " + cfg.title);
35521         }
35522         
35523         
35524         switch(cfg.xtype) 
35525         {
35526             case 'Content':  // ContentPanel (el, cfg)
35527             case 'Scroll':  // ContentPanel (el, cfg)
35528             case 'View': 
35529                 cfg.autoCreate = cfg.autoCreate || true;
35530                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35531                 //} else {
35532                 //    var el = this.el.createChild();
35533                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35534                 //}
35535                 
35536                 this.add(region, ret);
35537                 break;
35538             
35539             /*
35540             case 'TreePanel': // our new panel!
35541                 cfg.el = this.el.createChild();
35542                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35543                 this.add(region, ret);
35544                 break;
35545             */
35546             
35547             case 'Nest': 
35548                 // create a new Layout (which is  a Border Layout...
35549                 
35550                 var clayout = cfg.layout;
35551                 clayout.el  = this.el.createChild();
35552                 clayout.items   = clayout.items  || [];
35553                 
35554                 delete cfg.layout;
35555                 
35556                 // replace this exitems with the clayout ones..
35557                 xitems = clayout.items;
35558                  
35559                 // force background off if it's in center...
35560                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35561                     cfg.background = false;
35562                 }
35563                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35564                 
35565                 
35566                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35567                 //console.log('adding nested layout panel '  + cfg.toSource());
35568                 this.add(region, ret);
35569                 nb = {}; /// find first...
35570                 break;
35571             
35572             case 'Grid':
35573                 
35574                 // needs grid and region
35575                 
35576                 //var el = this.getRegion(region).el.createChild();
35577                 /*
35578                  *var el = this.el.createChild();
35579                 // create the grid first...
35580                 cfg.grid.container = el;
35581                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35582                 */
35583                 
35584                 if (region == 'center' && this.active ) {
35585                     cfg.background = false;
35586                 }
35587                 
35588                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35589                 
35590                 this.add(region, ret);
35591                 /*
35592                 if (cfg.background) {
35593                     // render grid on panel activation (if panel background)
35594                     ret.on('activate', function(gp) {
35595                         if (!gp.grid.rendered) {
35596                     //        gp.grid.render(el);
35597                         }
35598                     });
35599                 } else {
35600                   //  cfg.grid.render(el);
35601                 }
35602                 */
35603                 break;
35604            
35605            
35606             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35607                 // it was the old xcomponent building that caused this before.
35608                 // espeically if border is the top element in the tree.
35609                 ret = this;
35610                 break; 
35611                 
35612                     
35613                 
35614                 
35615                 
35616             default:
35617                 /*
35618                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35619                     
35620                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35621                     this.add(region, ret);
35622                 } else {
35623                 */
35624                     Roo.log(cfg);
35625                     throw "Can not add '" + cfg.xtype + "' to Border";
35626                     return null;
35627              
35628                                 
35629              
35630         }
35631         this.beginUpdate();
35632         // add children..
35633         var region = '';
35634         var abn = {};
35635         Roo.each(xitems, function(i)  {
35636             region = nb && i.region ? i.region : false;
35637             
35638             var add = ret.addxtype(i);
35639            
35640             if (region) {
35641                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35642                 if (!i.background) {
35643                     abn[region] = nb[region] ;
35644                 }
35645             }
35646             
35647         });
35648         this.endUpdate();
35649
35650         // make the last non-background panel active..
35651         //if (nb) { Roo.log(abn); }
35652         if (nb) {
35653             
35654             for(var r in abn) {
35655                 region = this.getRegion(r);
35656                 if (region) {
35657                     // tried using nb[r], but it does not work..
35658                      
35659                     region.showPanel(abn[r]);
35660                    
35661                 }
35662             }
35663         }
35664         return ret;
35665         
35666     },
35667     
35668     
35669 // private
35670     factory : function(cfg)
35671     {
35672         
35673         var validRegions = Roo.bootstrap.layout.Border.regions;
35674
35675         var target = cfg.region;
35676         cfg.mgr = this;
35677         
35678         var r = Roo.bootstrap.layout;
35679         Roo.log(target);
35680         switch(target){
35681             case "north":
35682                 return new r.North(cfg);
35683             case "south":
35684                 return new r.South(cfg);
35685             case "east":
35686                 return new r.East(cfg);
35687             case "west":
35688                 return new r.West(cfg);
35689             case "center":
35690                 return new r.Center(cfg);
35691         }
35692         throw 'Layout region "'+target+'" not supported.';
35693     }
35694     
35695     
35696 });
35697  /*
35698  * Based on:
35699  * Ext JS Library 1.1.1
35700  * Copyright(c) 2006-2007, Ext JS, LLC.
35701  *
35702  * Originally Released Under LGPL - original licence link has changed is not relivant.
35703  *
35704  * Fork - LGPL
35705  * <script type="text/javascript">
35706  */
35707  
35708 /**
35709  * @class Roo.bootstrap.layout.Basic
35710  * @extends Roo.util.Observable
35711  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35712  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35713  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35714  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35715  * @cfg {string}   region  the region that it inhabits..
35716  * @cfg {bool}   skipConfig skip config?
35717  * 
35718
35719  */
35720 Roo.bootstrap.layout.Basic = function(config){
35721     
35722     this.mgr = config.mgr;
35723     
35724     this.position = config.region;
35725     
35726     var skipConfig = config.skipConfig;
35727     
35728     this.events = {
35729         /**
35730          * @scope Roo.BasicLayoutRegion
35731          */
35732         
35733         /**
35734          * @event beforeremove
35735          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35736          * @param {Roo.LayoutRegion} this
35737          * @param {Roo.ContentPanel} panel The panel
35738          * @param {Object} e The cancel event object
35739          */
35740         "beforeremove" : true,
35741         /**
35742          * @event invalidated
35743          * Fires when the layout for this region is changed.
35744          * @param {Roo.LayoutRegion} this
35745          */
35746         "invalidated" : true,
35747         /**
35748          * @event visibilitychange
35749          * Fires when this region is shown or hidden 
35750          * @param {Roo.LayoutRegion} this
35751          * @param {Boolean} visibility true or false
35752          */
35753         "visibilitychange" : true,
35754         /**
35755          * @event paneladded
35756          * Fires when a panel is added. 
35757          * @param {Roo.LayoutRegion} this
35758          * @param {Roo.ContentPanel} panel The panel
35759          */
35760         "paneladded" : true,
35761         /**
35762          * @event panelremoved
35763          * Fires when a panel is removed. 
35764          * @param {Roo.LayoutRegion} this
35765          * @param {Roo.ContentPanel} panel The panel
35766          */
35767         "panelremoved" : true,
35768         /**
35769          * @event beforecollapse
35770          * Fires when this region before collapse.
35771          * @param {Roo.LayoutRegion} this
35772          */
35773         "beforecollapse" : true,
35774         /**
35775          * @event collapsed
35776          * Fires when this region is collapsed.
35777          * @param {Roo.LayoutRegion} this
35778          */
35779         "collapsed" : true,
35780         /**
35781          * @event expanded
35782          * Fires when this region is expanded.
35783          * @param {Roo.LayoutRegion} this
35784          */
35785         "expanded" : true,
35786         /**
35787          * @event slideshow
35788          * Fires when this region is slid into view.
35789          * @param {Roo.LayoutRegion} this
35790          */
35791         "slideshow" : true,
35792         /**
35793          * @event slidehide
35794          * Fires when this region slides out of view. 
35795          * @param {Roo.LayoutRegion} this
35796          */
35797         "slidehide" : true,
35798         /**
35799          * @event panelactivated
35800          * Fires when a panel is activated. 
35801          * @param {Roo.LayoutRegion} this
35802          * @param {Roo.ContentPanel} panel The activated panel
35803          */
35804         "panelactivated" : true,
35805         /**
35806          * @event resized
35807          * Fires when the user resizes this region. 
35808          * @param {Roo.LayoutRegion} this
35809          * @param {Number} newSize The new size (width for east/west, height for north/south)
35810          */
35811         "resized" : true
35812     };
35813     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35814     this.panels = new Roo.util.MixedCollection();
35815     this.panels.getKey = this.getPanelId.createDelegate(this);
35816     this.box = null;
35817     this.activePanel = null;
35818     // ensure listeners are added...
35819     
35820     if (config.listeners || config.events) {
35821         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35822             listeners : config.listeners || {},
35823             events : config.events || {}
35824         });
35825     }
35826     
35827     if(skipConfig !== true){
35828         this.applyConfig(config);
35829     }
35830 };
35831
35832 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35833 {
35834     getPanelId : function(p){
35835         return p.getId();
35836     },
35837     
35838     applyConfig : function(config){
35839         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35840         this.config = config;
35841         
35842     },
35843     
35844     /**
35845      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35846      * the width, for horizontal (north, south) the height.
35847      * @param {Number} newSize The new width or height
35848      */
35849     resizeTo : function(newSize){
35850         var el = this.el ? this.el :
35851                  (this.activePanel ? this.activePanel.getEl() : null);
35852         if(el){
35853             switch(this.position){
35854                 case "east":
35855                 case "west":
35856                     el.setWidth(newSize);
35857                     this.fireEvent("resized", this, newSize);
35858                 break;
35859                 case "north":
35860                 case "south":
35861                     el.setHeight(newSize);
35862                     this.fireEvent("resized", this, newSize);
35863                 break;                
35864             }
35865         }
35866     },
35867     
35868     getBox : function(){
35869         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35870     },
35871     
35872     getMargins : function(){
35873         return this.margins;
35874     },
35875     
35876     updateBox : function(box){
35877         this.box = box;
35878         var el = this.activePanel.getEl();
35879         el.dom.style.left = box.x + "px";
35880         el.dom.style.top = box.y + "px";
35881         this.activePanel.setSize(box.width, box.height);
35882     },
35883     
35884     /**
35885      * Returns the container element for this region.
35886      * @return {Roo.Element}
35887      */
35888     getEl : function(){
35889         return this.activePanel;
35890     },
35891     
35892     /**
35893      * Returns true if this region is currently visible.
35894      * @return {Boolean}
35895      */
35896     isVisible : function(){
35897         return this.activePanel ? true : false;
35898     },
35899     
35900     setActivePanel : function(panel){
35901         panel = this.getPanel(panel);
35902         if(this.activePanel && this.activePanel != panel){
35903             this.activePanel.setActiveState(false);
35904             this.activePanel.getEl().setLeftTop(-10000,-10000);
35905         }
35906         this.activePanel = panel;
35907         panel.setActiveState(true);
35908         if(this.box){
35909             panel.setSize(this.box.width, this.box.height);
35910         }
35911         this.fireEvent("panelactivated", this, panel);
35912         this.fireEvent("invalidated");
35913     },
35914     
35915     /**
35916      * Show the specified panel.
35917      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35918      * @return {Roo.ContentPanel} The shown panel or null
35919      */
35920     showPanel : function(panel){
35921         panel = this.getPanel(panel);
35922         if(panel){
35923             this.setActivePanel(panel);
35924         }
35925         return panel;
35926     },
35927     
35928     /**
35929      * Get the active panel for this region.
35930      * @return {Roo.ContentPanel} The active panel or null
35931      */
35932     getActivePanel : function(){
35933         return this.activePanel;
35934     },
35935     
35936     /**
35937      * Add the passed ContentPanel(s)
35938      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35939      * @return {Roo.ContentPanel} The panel added (if only one was added)
35940      */
35941     add : function(panel){
35942         if(arguments.length > 1){
35943             for(var i = 0, len = arguments.length; i < len; i++) {
35944                 this.add(arguments[i]);
35945             }
35946             return null;
35947         }
35948         if(this.hasPanel(panel)){
35949             this.showPanel(panel);
35950             return panel;
35951         }
35952         var el = panel.getEl();
35953         if(el.dom.parentNode != this.mgr.el.dom){
35954             this.mgr.el.dom.appendChild(el.dom);
35955         }
35956         if(panel.setRegion){
35957             panel.setRegion(this);
35958         }
35959         this.panels.add(panel);
35960         el.setStyle("position", "absolute");
35961         if(!panel.background){
35962             this.setActivePanel(panel);
35963             if(this.config.initialSize && this.panels.getCount()==1){
35964                 this.resizeTo(this.config.initialSize);
35965             }
35966         }
35967         this.fireEvent("paneladded", this, panel);
35968         return panel;
35969     },
35970     
35971     /**
35972      * Returns true if the panel is in this region.
35973      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35974      * @return {Boolean}
35975      */
35976     hasPanel : function(panel){
35977         if(typeof panel == "object"){ // must be panel obj
35978             panel = panel.getId();
35979         }
35980         return this.getPanel(panel) ? true : false;
35981     },
35982     
35983     /**
35984      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35985      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35986      * @param {Boolean} preservePanel Overrides the config preservePanel option
35987      * @return {Roo.ContentPanel} The panel that was removed
35988      */
35989     remove : function(panel, preservePanel){
35990         panel = this.getPanel(panel);
35991         if(!panel){
35992             return null;
35993         }
35994         var e = {};
35995         this.fireEvent("beforeremove", this, panel, e);
35996         if(e.cancel === true){
35997             return null;
35998         }
35999         var panelId = panel.getId();
36000         this.panels.removeKey(panelId);
36001         return panel;
36002     },
36003     
36004     /**
36005      * Returns the panel specified or null if it's not in this region.
36006      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
36007      * @return {Roo.ContentPanel}
36008      */
36009     getPanel : function(id){
36010         if(typeof id == "object"){ // must be panel obj
36011             return id;
36012         }
36013         return this.panels.get(id);
36014     },
36015     
36016     /**
36017      * Returns this regions position (north/south/east/west/center).
36018      * @return {String} 
36019      */
36020     getPosition: function(){
36021         return this.position;    
36022     }
36023 });/*
36024  * Based on:
36025  * Ext JS Library 1.1.1
36026  * Copyright(c) 2006-2007, Ext JS, LLC.
36027  *
36028  * Originally Released Under LGPL - original licence link has changed is not relivant.
36029  *
36030  * Fork - LGPL
36031  * <script type="text/javascript">
36032  */
36033  
36034 /**
36035  * @class Roo.bootstrap.layout.Region
36036  * @extends Roo.bootstrap.layout.Basic
36037  * This class represents a region in a layout manager.
36038  
36039  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
36040  * @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})
36041  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
36042  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
36043  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
36044  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
36045  * @cfg {String}    title           The title for the region (overrides panel titles)
36046  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
36047  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
36048  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
36049  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
36050  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
36051  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
36052  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
36053  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
36054  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
36055  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
36056
36057  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
36058  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
36059  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
36060  * @cfg {Number}    width           For East/West panels
36061  * @cfg {Number}    height          For North/South panels
36062  * @cfg {Boolean}   split           To show the splitter
36063  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
36064  * 
36065  * @cfg {string}   cls             Extra CSS classes to add to region
36066  * 
36067  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
36068  * @cfg {string}   region  the region that it inhabits..
36069  *
36070
36071  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
36072  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
36073
36074  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
36075  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
36076  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
36077  */
36078 Roo.bootstrap.layout.Region = function(config)
36079 {
36080     this.applyConfig(config);
36081
36082     var mgr = config.mgr;
36083     var pos = config.region;
36084     config.skipConfig = true;
36085     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
36086     
36087     if (mgr.el) {
36088         this.onRender(mgr.el);   
36089     }
36090      
36091     this.visible = true;
36092     this.collapsed = false;
36093     this.unrendered_panels = [];
36094 };
36095
36096 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36097
36098     position: '', // set by wrapper (eg. north/south etc..)
36099     unrendered_panels : null,  // unrendered panels.
36100     
36101     tabPosition : false,
36102     
36103     mgr: false, // points to 'Border'
36104     
36105     
36106     createBody : function(){
36107         /** This region's body element 
36108         * @type Roo.Element */
36109         this.bodyEl = this.el.createChild({
36110                 tag: "div",
36111                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36112         });
36113     },
36114
36115     onRender: function(ctr, pos)
36116     {
36117         var dh = Roo.DomHelper;
36118         /** This region's container element 
36119         * @type Roo.Element */
36120         this.el = dh.append(ctr.dom, {
36121                 tag: "div",
36122                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36123             }, true);
36124         /** This region's title element 
36125         * @type Roo.Element */
36126     
36127         this.titleEl = dh.append(this.el.dom,  {
36128                 tag: "div",
36129                 unselectable: "on",
36130                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36131                 children:[
36132                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
36133                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36134                 ]
36135             }, true);
36136         
36137         this.titleEl.enableDisplayMode();
36138         /** This region's title text element 
36139         * @type HTMLElement */
36140         this.titleTextEl = this.titleEl.dom.firstChild;
36141         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36142         /*
36143         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36144         this.closeBtn.enableDisplayMode();
36145         this.closeBtn.on("click", this.closeClicked, this);
36146         this.closeBtn.hide();
36147     */
36148         this.createBody(this.config);
36149         if(this.config.hideWhenEmpty){
36150             this.hide();
36151             this.on("paneladded", this.validateVisibility, this);
36152             this.on("panelremoved", this.validateVisibility, this);
36153         }
36154         if(this.autoScroll){
36155             this.bodyEl.setStyle("overflow", "auto");
36156         }else{
36157             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36158         }
36159         //if(c.titlebar !== false){
36160             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36161                 this.titleEl.hide();
36162             }else{
36163                 this.titleEl.show();
36164                 if(this.config.title){
36165                     this.titleTextEl.innerHTML = this.config.title;
36166                 }
36167             }
36168         //}
36169         if(this.config.collapsed){
36170             this.collapse(true);
36171         }
36172         if(this.config.hidden){
36173             this.hide();
36174         }
36175         
36176         if (this.unrendered_panels && this.unrendered_panels.length) {
36177             for (var i =0;i< this.unrendered_panels.length; i++) {
36178                 this.add(this.unrendered_panels[i]);
36179             }
36180             this.unrendered_panels = null;
36181             
36182         }
36183         
36184     },
36185     
36186     applyConfig : function(c)
36187     {
36188         /*
36189          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36190             var dh = Roo.DomHelper;
36191             if(c.titlebar !== false){
36192                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36193                 this.collapseBtn.on("click", this.collapse, this);
36194                 this.collapseBtn.enableDisplayMode();
36195                 /*
36196                 if(c.showPin === true || this.showPin){
36197                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36198                     this.stickBtn.enableDisplayMode();
36199                     this.stickBtn.on("click", this.expand, this);
36200                     this.stickBtn.hide();
36201                 }
36202                 
36203             }
36204             */
36205             /** This region's collapsed element
36206             * @type Roo.Element */
36207             /*
36208              *
36209             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36210                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36211             ]}, true);
36212             
36213             if(c.floatable !== false){
36214                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36215                this.collapsedEl.on("click", this.collapseClick, this);
36216             }
36217
36218             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36219                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36220                    id: "message", unselectable: "on", style:{"float":"left"}});
36221                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36222              }
36223             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36224             this.expandBtn.on("click", this.expand, this);
36225             
36226         }
36227         
36228         if(this.collapseBtn){
36229             this.collapseBtn.setVisible(c.collapsible == true);
36230         }
36231         
36232         this.cmargins = c.cmargins || this.cmargins ||
36233                          (this.position == "west" || this.position == "east" ?
36234                              {top: 0, left: 2, right:2, bottom: 0} :
36235                              {top: 2, left: 0, right:0, bottom: 2});
36236         */
36237         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36238         
36239         
36240         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36241         
36242         this.autoScroll = c.autoScroll || false;
36243         
36244         
36245        
36246         
36247         this.duration = c.duration || .30;
36248         this.slideDuration = c.slideDuration || .45;
36249         this.config = c;
36250        
36251     },
36252     /**
36253      * Returns true if this region is currently visible.
36254      * @return {Boolean}
36255      */
36256     isVisible : function(){
36257         return this.visible;
36258     },
36259
36260     /**
36261      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36262      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36263      */
36264     //setCollapsedTitle : function(title){
36265     //    title = title || "&#160;";
36266      //   if(this.collapsedTitleTextEl){
36267       //      this.collapsedTitleTextEl.innerHTML = title;
36268        // }
36269     //},
36270
36271     getBox : function(){
36272         var b;
36273       //  if(!this.collapsed){
36274             b = this.el.getBox(false, true);
36275        // }else{
36276           //  b = this.collapsedEl.getBox(false, true);
36277         //}
36278         return b;
36279     },
36280
36281     getMargins : function(){
36282         return this.margins;
36283         //return this.collapsed ? this.cmargins : this.margins;
36284     },
36285 /*
36286     highlight : function(){
36287         this.el.addClass("x-layout-panel-dragover");
36288     },
36289
36290     unhighlight : function(){
36291         this.el.removeClass("x-layout-panel-dragover");
36292     },
36293 */
36294     updateBox : function(box)
36295     {
36296         if (!this.bodyEl) {
36297             return; // not rendered yet..
36298         }
36299         
36300         this.box = box;
36301         if(!this.collapsed){
36302             this.el.dom.style.left = box.x + "px";
36303             this.el.dom.style.top = box.y + "px";
36304             this.updateBody(box.width, box.height);
36305         }else{
36306             this.collapsedEl.dom.style.left = box.x + "px";
36307             this.collapsedEl.dom.style.top = box.y + "px";
36308             this.collapsedEl.setSize(box.width, box.height);
36309         }
36310         if(this.tabs){
36311             this.tabs.autoSizeTabs();
36312         }
36313     },
36314
36315     updateBody : function(w, h)
36316     {
36317         if(w !== null){
36318             this.el.setWidth(w);
36319             w -= this.el.getBorderWidth("rl");
36320             if(this.config.adjustments){
36321                 w += this.config.adjustments[0];
36322             }
36323         }
36324         if(h !== null && h > 0){
36325             this.el.setHeight(h);
36326             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36327             h -= this.el.getBorderWidth("tb");
36328             if(this.config.adjustments){
36329                 h += this.config.adjustments[1];
36330             }
36331             this.bodyEl.setHeight(h);
36332             if(this.tabs){
36333                 h = this.tabs.syncHeight(h);
36334             }
36335         }
36336         if(this.panelSize){
36337             w = w !== null ? w : this.panelSize.width;
36338             h = h !== null ? h : this.panelSize.height;
36339         }
36340         if(this.activePanel){
36341             var el = this.activePanel.getEl();
36342             w = w !== null ? w : el.getWidth();
36343             h = h !== null ? h : el.getHeight();
36344             this.panelSize = {width: w, height: h};
36345             this.activePanel.setSize(w, h);
36346         }
36347         if(Roo.isIE && this.tabs){
36348             this.tabs.el.repaint();
36349         }
36350     },
36351
36352     /**
36353      * Returns the container element for this region.
36354      * @return {Roo.Element}
36355      */
36356     getEl : function(){
36357         return this.el;
36358     },
36359
36360     /**
36361      * Hides this region.
36362      */
36363     hide : function(){
36364         //if(!this.collapsed){
36365             this.el.dom.style.left = "-2000px";
36366             this.el.hide();
36367         //}else{
36368          //   this.collapsedEl.dom.style.left = "-2000px";
36369          //   this.collapsedEl.hide();
36370        // }
36371         this.visible = false;
36372         this.fireEvent("visibilitychange", this, false);
36373     },
36374
36375     /**
36376      * Shows this region if it was previously hidden.
36377      */
36378     show : function(){
36379         //if(!this.collapsed){
36380             this.el.show();
36381         //}else{
36382         //    this.collapsedEl.show();
36383        // }
36384         this.visible = true;
36385         this.fireEvent("visibilitychange", this, true);
36386     },
36387 /*
36388     closeClicked : function(){
36389         if(this.activePanel){
36390             this.remove(this.activePanel);
36391         }
36392     },
36393
36394     collapseClick : function(e){
36395         if(this.isSlid){
36396            e.stopPropagation();
36397            this.slideIn();
36398         }else{
36399            e.stopPropagation();
36400            this.slideOut();
36401         }
36402     },
36403 */
36404     /**
36405      * Collapses this region.
36406      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36407      */
36408     /*
36409     collapse : function(skipAnim, skipCheck = false){
36410         if(this.collapsed) {
36411             return;
36412         }
36413         
36414         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36415             
36416             this.collapsed = true;
36417             if(this.split){
36418                 this.split.el.hide();
36419             }
36420             if(this.config.animate && skipAnim !== true){
36421                 this.fireEvent("invalidated", this);
36422                 this.animateCollapse();
36423             }else{
36424                 this.el.setLocation(-20000,-20000);
36425                 this.el.hide();
36426                 this.collapsedEl.show();
36427                 this.fireEvent("collapsed", this);
36428                 this.fireEvent("invalidated", this);
36429             }
36430         }
36431         
36432     },
36433 */
36434     animateCollapse : function(){
36435         // overridden
36436     },
36437
36438     /**
36439      * Expands this region if it was previously collapsed.
36440      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36441      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36442      */
36443     /*
36444     expand : function(e, skipAnim){
36445         if(e) {
36446             e.stopPropagation();
36447         }
36448         if(!this.collapsed || this.el.hasActiveFx()) {
36449             return;
36450         }
36451         if(this.isSlid){
36452             this.afterSlideIn();
36453             skipAnim = true;
36454         }
36455         this.collapsed = false;
36456         if(this.config.animate && skipAnim !== true){
36457             this.animateExpand();
36458         }else{
36459             this.el.show();
36460             if(this.split){
36461                 this.split.el.show();
36462             }
36463             this.collapsedEl.setLocation(-2000,-2000);
36464             this.collapsedEl.hide();
36465             this.fireEvent("invalidated", this);
36466             this.fireEvent("expanded", this);
36467         }
36468     },
36469 */
36470     animateExpand : function(){
36471         // overridden
36472     },
36473
36474     initTabs : function()
36475     {
36476         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36477         
36478         var ts = new Roo.bootstrap.panel.Tabs({
36479             el: this.bodyEl.dom,
36480             region : this,
36481             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36482             disableTooltips: this.config.disableTabTips,
36483             toolbar : this.config.toolbar
36484         });
36485         
36486         if(this.config.hideTabs){
36487             ts.stripWrap.setDisplayed(false);
36488         }
36489         this.tabs = ts;
36490         ts.resizeTabs = this.config.resizeTabs === true;
36491         ts.minTabWidth = this.config.minTabWidth || 40;
36492         ts.maxTabWidth = this.config.maxTabWidth || 250;
36493         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36494         ts.monitorResize = false;
36495         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36496         ts.bodyEl.addClass('roo-layout-tabs-body');
36497         this.panels.each(this.initPanelAsTab, this);
36498     },
36499
36500     initPanelAsTab : function(panel){
36501         var ti = this.tabs.addTab(
36502             panel.getEl().id,
36503             panel.getTitle(),
36504             null,
36505             this.config.closeOnTab && panel.isClosable(),
36506             panel.tpl
36507         );
36508         if(panel.tabTip !== undefined){
36509             ti.setTooltip(panel.tabTip);
36510         }
36511         ti.on("activate", function(){
36512               this.setActivePanel(panel);
36513         }, this);
36514         
36515         if(this.config.closeOnTab){
36516             ti.on("beforeclose", function(t, e){
36517                 e.cancel = true;
36518                 this.remove(panel);
36519             }, this);
36520         }
36521         
36522         panel.tabItem = ti;
36523         
36524         return ti;
36525     },
36526
36527     updatePanelTitle : function(panel, title)
36528     {
36529         if(this.activePanel == panel){
36530             this.updateTitle(title);
36531         }
36532         if(this.tabs){
36533             var ti = this.tabs.getTab(panel.getEl().id);
36534             ti.setText(title);
36535             if(panel.tabTip !== undefined){
36536                 ti.setTooltip(panel.tabTip);
36537             }
36538         }
36539     },
36540
36541     updateTitle : function(title){
36542         if(this.titleTextEl && !this.config.title){
36543             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36544         }
36545     },
36546
36547     setActivePanel : function(panel)
36548     {
36549         panel = this.getPanel(panel);
36550         if(this.activePanel && this.activePanel != panel){
36551             if(this.activePanel.setActiveState(false) === false){
36552                 return;
36553             }
36554         }
36555         this.activePanel = panel;
36556         panel.setActiveState(true);
36557         if(this.panelSize){
36558             panel.setSize(this.panelSize.width, this.panelSize.height);
36559         }
36560         if(this.closeBtn){
36561             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36562         }
36563         this.updateTitle(panel.getTitle());
36564         if(this.tabs){
36565             this.fireEvent("invalidated", this);
36566         }
36567         this.fireEvent("panelactivated", this, panel);
36568     },
36569
36570     /**
36571      * Shows the specified panel.
36572      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36573      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36574      */
36575     showPanel : function(panel)
36576     {
36577         panel = this.getPanel(panel);
36578         if(panel){
36579             if(this.tabs){
36580                 var tab = this.tabs.getTab(panel.getEl().id);
36581                 if(tab.isHidden()){
36582                     this.tabs.unhideTab(tab.id);
36583                 }
36584                 tab.activate();
36585             }else{
36586                 this.setActivePanel(panel);
36587             }
36588         }
36589         return panel;
36590     },
36591
36592     /**
36593      * Get the active panel for this region.
36594      * @return {Roo.ContentPanel} The active panel or null
36595      */
36596     getActivePanel : function(){
36597         return this.activePanel;
36598     },
36599
36600     validateVisibility : function(){
36601         if(this.panels.getCount() < 1){
36602             this.updateTitle("&#160;");
36603             this.closeBtn.hide();
36604             this.hide();
36605         }else{
36606             if(!this.isVisible()){
36607                 this.show();
36608             }
36609         }
36610     },
36611
36612     /**
36613      * Adds the passed ContentPanel(s) to this region.
36614      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36615      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36616      */
36617     add : function(panel)
36618     {
36619         if(arguments.length > 1){
36620             for(var i = 0, len = arguments.length; i < len; i++) {
36621                 this.add(arguments[i]);
36622             }
36623             return null;
36624         }
36625         
36626         // if we have not been rendered yet, then we can not really do much of this..
36627         if (!this.bodyEl) {
36628             this.unrendered_panels.push(panel);
36629             return panel;
36630         }
36631         
36632         
36633         
36634         
36635         if(this.hasPanel(panel)){
36636             this.showPanel(panel);
36637             return panel;
36638         }
36639         panel.setRegion(this);
36640         this.panels.add(panel);
36641        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36642             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36643             // and hide them... ???
36644             this.bodyEl.dom.appendChild(panel.getEl().dom);
36645             if(panel.background !== true){
36646                 this.setActivePanel(panel);
36647             }
36648             this.fireEvent("paneladded", this, panel);
36649             return panel;
36650         }
36651         */
36652         if(!this.tabs){
36653             this.initTabs();
36654         }else{
36655             this.initPanelAsTab(panel);
36656         }
36657         
36658         
36659         if(panel.background !== true){
36660             this.tabs.activate(panel.getEl().id);
36661         }
36662         this.fireEvent("paneladded", this, panel);
36663         return panel;
36664     },
36665
36666     /**
36667      * Hides the tab for the specified panel.
36668      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36669      */
36670     hidePanel : function(panel){
36671         if(this.tabs && (panel = this.getPanel(panel))){
36672             this.tabs.hideTab(panel.getEl().id);
36673         }
36674     },
36675
36676     /**
36677      * Unhides the tab for a previously hidden panel.
36678      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36679      */
36680     unhidePanel : function(panel){
36681         if(this.tabs && (panel = this.getPanel(panel))){
36682             this.tabs.unhideTab(panel.getEl().id);
36683         }
36684     },
36685
36686     clearPanels : function(){
36687         while(this.panels.getCount() > 0){
36688              this.remove(this.panels.first());
36689         }
36690     },
36691
36692     /**
36693      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36694      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36695      * @param {Boolean} preservePanel Overrides the config preservePanel option
36696      * @return {Roo.ContentPanel} The panel that was removed
36697      */
36698     remove : function(panel, preservePanel)
36699     {
36700         panel = this.getPanel(panel);
36701         if(!panel){
36702             return null;
36703         }
36704         var e = {};
36705         this.fireEvent("beforeremove", this, panel, e);
36706         if(e.cancel === true){
36707             return null;
36708         }
36709         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36710         var panelId = panel.getId();
36711         this.panels.removeKey(panelId);
36712         if(preservePanel){
36713             document.body.appendChild(panel.getEl().dom);
36714         }
36715         if(this.tabs){
36716             this.tabs.removeTab(panel.getEl().id);
36717         }else if (!preservePanel){
36718             this.bodyEl.dom.removeChild(panel.getEl().dom);
36719         }
36720         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36721             var p = this.panels.first();
36722             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36723             tempEl.appendChild(p.getEl().dom);
36724             this.bodyEl.update("");
36725             this.bodyEl.dom.appendChild(p.getEl().dom);
36726             tempEl = null;
36727             this.updateTitle(p.getTitle());
36728             this.tabs = null;
36729             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36730             this.setActivePanel(p);
36731         }
36732         panel.setRegion(null);
36733         if(this.activePanel == panel){
36734             this.activePanel = null;
36735         }
36736         if(this.config.autoDestroy !== false && preservePanel !== true){
36737             try{panel.destroy();}catch(e){}
36738         }
36739         this.fireEvent("panelremoved", this, panel);
36740         return panel;
36741     },
36742
36743     /**
36744      * Returns the TabPanel component used by this region
36745      * @return {Roo.TabPanel}
36746      */
36747     getTabs : function(){
36748         return this.tabs;
36749     },
36750
36751     createTool : function(parentEl, className){
36752         var btn = Roo.DomHelper.append(parentEl, {
36753             tag: "div",
36754             cls: "x-layout-tools-button",
36755             children: [ {
36756                 tag: "div",
36757                 cls: "roo-layout-tools-button-inner " + className,
36758                 html: "&#160;"
36759             }]
36760         }, true);
36761         btn.addClassOnOver("roo-layout-tools-button-over");
36762         return btn;
36763     }
36764 });/*
36765  * Based on:
36766  * Ext JS Library 1.1.1
36767  * Copyright(c) 2006-2007, Ext JS, LLC.
36768  *
36769  * Originally Released Under LGPL - original licence link has changed is not relivant.
36770  *
36771  * Fork - LGPL
36772  * <script type="text/javascript">
36773  */
36774  
36775
36776
36777 /**
36778  * @class Roo.SplitLayoutRegion
36779  * @extends Roo.LayoutRegion
36780  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36781  */
36782 Roo.bootstrap.layout.Split = function(config){
36783     this.cursor = config.cursor;
36784     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36785 };
36786
36787 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36788 {
36789     splitTip : "Drag to resize.",
36790     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36791     useSplitTips : false,
36792
36793     applyConfig : function(config){
36794         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36795     },
36796     
36797     onRender : function(ctr,pos) {
36798         
36799         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36800         if(!this.config.split){
36801             return;
36802         }
36803         if(!this.split){
36804             
36805             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36806                             tag: "div",
36807                             id: this.el.id + "-split",
36808                             cls: "roo-layout-split roo-layout-split-"+this.position,
36809                             html: "&#160;"
36810             });
36811             /** The SplitBar for this region 
36812             * @type Roo.SplitBar */
36813             // does not exist yet...
36814             Roo.log([this.position, this.orientation]);
36815             
36816             this.split = new Roo.bootstrap.SplitBar({
36817                 dragElement : splitEl,
36818                 resizingElement: this.el,
36819                 orientation : this.orientation
36820             });
36821             
36822             this.split.on("moved", this.onSplitMove, this);
36823             this.split.useShim = this.config.useShim === true;
36824             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36825             if(this.useSplitTips){
36826                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36827             }
36828             //if(config.collapsible){
36829             //    this.split.el.on("dblclick", this.collapse,  this);
36830             //}
36831         }
36832         if(typeof this.config.minSize != "undefined"){
36833             this.split.minSize = this.config.minSize;
36834         }
36835         if(typeof this.config.maxSize != "undefined"){
36836             this.split.maxSize = this.config.maxSize;
36837         }
36838         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36839             this.hideSplitter();
36840         }
36841         
36842     },
36843
36844     getHMaxSize : function(){
36845          var cmax = this.config.maxSize || 10000;
36846          var center = this.mgr.getRegion("center");
36847          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36848     },
36849
36850     getVMaxSize : function(){
36851          var cmax = this.config.maxSize || 10000;
36852          var center = this.mgr.getRegion("center");
36853          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36854     },
36855
36856     onSplitMove : function(split, newSize){
36857         this.fireEvent("resized", this, newSize);
36858     },
36859     
36860     /** 
36861      * Returns the {@link Roo.SplitBar} for this region.
36862      * @return {Roo.SplitBar}
36863      */
36864     getSplitBar : function(){
36865         return this.split;
36866     },
36867     
36868     hide : function(){
36869         this.hideSplitter();
36870         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36871     },
36872
36873     hideSplitter : function(){
36874         if(this.split){
36875             this.split.el.setLocation(-2000,-2000);
36876             this.split.el.hide();
36877         }
36878     },
36879
36880     show : function(){
36881         if(this.split){
36882             this.split.el.show();
36883         }
36884         Roo.bootstrap.layout.Split.superclass.show.call(this);
36885     },
36886     
36887     beforeSlide: function(){
36888         if(Roo.isGecko){// firefox overflow auto bug workaround
36889             this.bodyEl.clip();
36890             if(this.tabs) {
36891                 this.tabs.bodyEl.clip();
36892             }
36893             if(this.activePanel){
36894                 this.activePanel.getEl().clip();
36895                 
36896                 if(this.activePanel.beforeSlide){
36897                     this.activePanel.beforeSlide();
36898                 }
36899             }
36900         }
36901     },
36902     
36903     afterSlide : function(){
36904         if(Roo.isGecko){// firefox overflow auto bug workaround
36905             this.bodyEl.unclip();
36906             if(this.tabs) {
36907                 this.tabs.bodyEl.unclip();
36908             }
36909             if(this.activePanel){
36910                 this.activePanel.getEl().unclip();
36911                 if(this.activePanel.afterSlide){
36912                     this.activePanel.afterSlide();
36913                 }
36914             }
36915         }
36916     },
36917
36918     initAutoHide : function(){
36919         if(this.autoHide !== false){
36920             if(!this.autoHideHd){
36921                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36922                 this.autoHideHd = {
36923                     "mouseout": function(e){
36924                         if(!e.within(this.el, true)){
36925                             st.delay(500);
36926                         }
36927                     },
36928                     "mouseover" : function(e){
36929                         st.cancel();
36930                     },
36931                     scope : this
36932                 };
36933             }
36934             this.el.on(this.autoHideHd);
36935         }
36936     },
36937
36938     clearAutoHide : function(){
36939         if(this.autoHide !== false){
36940             this.el.un("mouseout", this.autoHideHd.mouseout);
36941             this.el.un("mouseover", this.autoHideHd.mouseover);
36942         }
36943     },
36944
36945     clearMonitor : function(){
36946         Roo.get(document).un("click", this.slideInIf, this);
36947     },
36948
36949     // these names are backwards but not changed for compat
36950     slideOut : function(){
36951         if(this.isSlid || this.el.hasActiveFx()){
36952             return;
36953         }
36954         this.isSlid = true;
36955         if(this.collapseBtn){
36956             this.collapseBtn.hide();
36957         }
36958         this.closeBtnState = this.closeBtn.getStyle('display');
36959         this.closeBtn.hide();
36960         if(this.stickBtn){
36961             this.stickBtn.show();
36962         }
36963         this.el.show();
36964         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36965         this.beforeSlide();
36966         this.el.setStyle("z-index", 10001);
36967         this.el.slideIn(this.getSlideAnchor(), {
36968             callback: function(){
36969                 this.afterSlide();
36970                 this.initAutoHide();
36971                 Roo.get(document).on("click", this.slideInIf, this);
36972                 this.fireEvent("slideshow", this);
36973             },
36974             scope: this,
36975             block: true
36976         });
36977     },
36978
36979     afterSlideIn : function(){
36980         this.clearAutoHide();
36981         this.isSlid = false;
36982         this.clearMonitor();
36983         this.el.setStyle("z-index", "");
36984         if(this.collapseBtn){
36985             this.collapseBtn.show();
36986         }
36987         this.closeBtn.setStyle('display', this.closeBtnState);
36988         if(this.stickBtn){
36989             this.stickBtn.hide();
36990         }
36991         this.fireEvent("slidehide", this);
36992     },
36993
36994     slideIn : function(cb){
36995         if(!this.isSlid || this.el.hasActiveFx()){
36996             Roo.callback(cb);
36997             return;
36998         }
36999         this.isSlid = false;
37000         this.beforeSlide();
37001         this.el.slideOut(this.getSlideAnchor(), {
37002             callback: function(){
37003                 this.el.setLeftTop(-10000, -10000);
37004                 this.afterSlide();
37005                 this.afterSlideIn();
37006                 Roo.callback(cb);
37007             },
37008             scope: this,
37009             block: true
37010         });
37011     },
37012     
37013     slideInIf : function(e){
37014         if(!e.within(this.el)){
37015             this.slideIn();
37016         }
37017     },
37018
37019     animateCollapse : function(){
37020         this.beforeSlide();
37021         this.el.setStyle("z-index", 20000);
37022         var anchor = this.getSlideAnchor();
37023         this.el.slideOut(anchor, {
37024             callback : function(){
37025                 this.el.setStyle("z-index", "");
37026                 this.collapsedEl.slideIn(anchor, {duration:.3});
37027                 this.afterSlide();
37028                 this.el.setLocation(-10000,-10000);
37029                 this.el.hide();
37030                 this.fireEvent("collapsed", this);
37031             },
37032             scope: this,
37033             block: true
37034         });
37035     },
37036
37037     animateExpand : function(){
37038         this.beforeSlide();
37039         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
37040         this.el.setStyle("z-index", 20000);
37041         this.collapsedEl.hide({
37042             duration:.1
37043         });
37044         this.el.slideIn(this.getSlideAnchor(), {
37045             callback : function(){
37046                 this.el.setStyle("z-index", "");
37047                 this.afterSlide();
37048                 if(this.split){
37049                     this.split.el.show();
37050                 }
37051                 this.fireEvent("invalidated", this);
37052                 this.fireEvent("expanded", this);
37053             },
37054             scope: this,
37055             block: true
37056         });
37057     },
37058
37059     anchors : {
37060         "west" : "left",
37061         "east" : "right",
37062         "north" : "top",
37063         "south" : "bottom"
37064     },
37065
37066     sanchors : {
37067         "west" : "l",
37068         "east" : "r",
37069         "north" : "t",
37070         "south" : "b"
37071     },
37072
37073     canchors : {
37074         "west" : "tl-tr",
37075         "east" : "tr-tl",
37076         "north" : "tl-bl",
37077         "south" : "bl-tl"
37078     },
37079
37080     getAnchor : function(){
37081         return this.anchors[this.position];
37082     },
37083
37084     getCollapseAnchor : function(){
37085         return this.canchors[this.position];
37086     },
37087
37088     getSlideAnchor : function(){
37089         return this.sanchors[this.position];
37090     },
37091
37092     getAlignAdj : function(){
37093         var cm = this.cmargins;
37094         switch(this.position){
37095             case "west":
37096                 return [0, 0];
37097             break;
37098             case "east":
37099                 return [0, 0];
37100             break;
37101             case "north":
37102                 return [0, 0];
37103             break;
37104             case "south":
37105                 return [0, 0];
37106             break;
37107         }
37108     },
37109
37110     getExpandAdj : function(){
37111         var c = this.collapsedEl, cm = this.cmargins;
37112         switch(this.position){
37113             case "west":
37114                 return [-(cm.right+c.getWidth()+cm.left), 0];
37115             break;
37116             case "east":
37117                 return [cm.right+c.getWidth()+cm.left, 0];
37118             break;
37119             case "north":
37120                 return [0, -(cm.top+cm.bottom+c.getHeight())];
37121             break;
37122             case "south":
37123                 return [0, cm.top+cm.bottom+c.getHeight()];
37124             break;
37125         }
37126     }
37127 });/*
37128  * Based on:
37129  * Ext JS Library 1.1.1
37130  * Copyright(c) 2006-2007, Ext JS, LLC.
37131  *
37132  * Originally Released Under LGPL - original licence link has changed is not relivant.
37133  *
37134  * Fork - LGPL
37135  * <script type="text/javascript">
37136  */
37137 /*
37138  * These classes are private internal classes
37139  */
37140 Roo.bootstrap.layout.Center = function(config){
37141     config.region = "center";
37142     Roo.bootstrap.layout.Region.call(this, config);
37143     this.visible = true;
37144     this.minWidth = config.minWidth || 20;
37145     this.minHeight = config.minHeight || 20;
37146 };
37147
37148 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37149     hide : function(){
37150         // center panel can't be hidden
37151     },
37152     
37153     show : function(){
37154         // center panel can't be hidden
37155     },
37156     
37157     getMinWidth: function(){
37158         return this.minWidth;
37159     },
37160     
37161     getMinHeight: function(){
37162         return this.minHeight;
37163     }
37164 });
37165
37166
37167
37168
37169  
37170
37171
37172
37173
37174
37175
37176 Roo.bootstrap.layout.North = function(config)
37177 {
37178     config.region = 'north';
37179     config.cursor = 'n-resize';
37180     
37181     Roo.bootstrap.layout.Split.call(this, config);
37182     
37183     
37184     if(this.split){
37185         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37186         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37187         this.split.el.addClass("roo-layout-split-v");
37188     }
37189     var size = config.initialSize || config.height;
37190     if(typeof size != "undefined"){
37191         this.el.setHeight(size);
37192     }
37193 };
37194 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37195 {
37196     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37197     
37198     
37199     
37200     getBox : function(){
37201         if(this.collapsed){
37202             return this.collapsedEl.getBox();
37203         }
37204         var box = this.el.getBox();
37205         if(this.split){
37206             box.height += this.split.el.getHeight();
37207         }
37208         return box;
37209     },
37210     
37211     updateBox : function(box){
37212         if(this.split && !this.collapsed){
37213             box.height -= this.split.el.getHeight();
37214             this.split.el.setLeft(box.x);
37215             this.split.el.setTop(box.y+box.height);
37216             this.split.el.setWidth(box.width);
37217         }
37218         if(this.collapsed){
37219             this.updateBody(box.width, null);
37220         }
37221         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37222     }
37223 });
37224
37225
37226
37227
37228
37229 Roo.bootstrap.layout.South = function(config){
37230     config.region = 'south';
37231     config.cursor = 's-resize';
37232     Roo.bootstrap.layout.Split.call(this, config);
37233     if(this.split){
37234         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37235         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37236         this.split.el.addClass("roo-layout-split-v");
37237     }
37238     var size = config.initialSize || config.height;
37239     if(typeof size != "undefined"){
37240         this.el.setHeight(size);
37241     }
37242 };
37243
37244 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37245     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37246     getBox : function(){
37247         if(this.collapsed){
37248             return this.collapsedEl.getBox();
37249         }
37250         var box = this.el.getBox();
37251         if(this.split){
37252             var sh = this.split.el.getHeight();
37253             box.height += sh;
37254             box.y -= sh;
37255         }
37256         return box;
37257     },
37258     
37259     updateBox : function(box){
37260         if(this.split && !this.collapsed){
37261             var sh = this.split.el.getHeight();
37262             box.height -= sh;
37263             box.y += sh;
37264             this.split.el.setLeft(box.x);
37265             this.split.el.setTop(box.y-sh);
37266             this.split.el.setWidth(box.width);
37267         }
37268         if(this.collapsed){
37269             this.updateBody(box.width, null);
37270         }
37271         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37272     }
37273 });
37274
37275 Roo.bootstrap.layout.East = function(config){
37276     config.region = "east";
37277     config.cursor = "e-resize";
37278     Roo.bootstrap.layout.Split.call(this, config);
37279     if(this.split){
37280         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37281         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37282         this.split.el.addClass("roo-layout-split-h");
37283     }
37284     var size = config.initialSize || config.width;
37285     if(typeof size != "undefined"){
37286         this.el.setWidth(size);
37287     }
37288 };
37289 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37290     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37291     getBox : function(){
37292         if(this.collapsed){
37293             return this.collapsedEl.getBox();
37294         }
37295         var box = this.el.getBox();
37296         if(this.split){
37297             var sw = this.split.el.getWidth();
37298             box.width += sw;
37299             box.x -= sw;
37300         }
37301         return box;
37302     },
37303
37304     updateBox : function(box){
37305         if(this.split && !this.collapsed){
37306             var sw = this.split.el.getWidth();
37307             box.width -= sw;
37308             this.split.el.setLeft(box.x);
37309             this.split.el.setTop(box.y);
37310             this.split.el.setHeight(box.height);
37311             box.x += sw;
37312         }
37313         if(this.collapsed){
37314             this.updateBody(null, box.height);
37315         }
37316         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37317     }
37318 });
37319
37320 Roo.bootstrap.layout.West = function(config){
37321     config.region = "west";
37322     config.cursor = "w-resize";
37323     
37324     Roo.bootstrap.layout.Split.call(this, config);
37325     if(this.split){
37326         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37327         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37328         this.split.el.addClass("roo-layout-split-h");
37329     }
37330     
37331 };
37332 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37333     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37334     
37335     onRender: function(ctr, pos)
37336     {
37337         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37338         var size = this.config.initialSize || this.config.width;
37339         if(typeof size != "undefined"){
37340             this.el.setWidth(size);
37341         }
37342     },
37343     
37344     getBox : function(){
37345         if(this.collapsed){
37346             return this.collapsedEl.getBox();
37347         }
37348         var box = this.el.getBox();
37349         if(this.split){
37350             box.width += this.split.el.getWidth();
37351         }
37352         return box;
37353     },
37354     
37355     updateBox : function(box){
37356         if(this.split && !this.collapsed){
37357             var sw = this.split.el.getWidth();
37358             box.width -= sw;
37359             this.split.el.setLeft(box.x+box.width);
37360             this.split.el.setTop(box.y);
37361             this.split.el.setHeight(box.height);
37362         }
37363         if(this.collapsed){
37364             this.updateBody(null, box.height);
37365         }
37366         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37367     }
37368 });Roo.namespace("Roo.bootstrap.panel");/*
37369  * Based on:
37370  * Ext JS Library 1.1.1
37371  * Copyright(c) 2006-2007, Ext JS, LLC.
37372  *
37373  * Originally Released Under LGPL - original licence link has changed is not relivant.
37374  *
37375  * Fork - LGPL
37376  * <script type="text/javascript">
37377  */
37378 /**
37379  * @class Roo.ContentPanel
37380  * @extends Roo.util.Observable
37381  * A basic ContentPanel element.
37382  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37383  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37384  * @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
37385  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37386  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37387  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37388  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37389  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37390  * @cfg {String} title          The title for this panel
37391  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37392  * @cfg {String} url            Calls {@link #setUrl} with this value
37393  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37394  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37395  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37396  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37397  * @cfg {Boolean} badges render the badges
37398
37399  * @constructor
37400  * Create a new ContentPanel.
37401  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37402  * @param {String/Object} config A string to set only the title or a config object
37403  * @param {String} content (optional) Set the HTML content for this panel
37404  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37405  */
37406 Roo.bootstrap.panel.Content = function( config){
37407     
37408     this.tpl = config.tpl || false;
37409     
37410     var el = config.el;
37411     var content = config.content;
37412
37413     if(config.autoCreate){ // xtype is available if this is called from factory
37414         el = Roo.id();
37415     }
37416     this.el = Roo.get(el);
37417     if(!this.el && config && config.autoCreate){
37418         if(typeof config.autoCreate == "object"){
37419             if(!config.autoCreate.id){
37420                 config.autoCreate.id = config.id||el;
37421             }
37422             this.el = Roo.DomHelper.append(document.body,
37423                         config.autoCreate, true);
37424         }else{
37425             var elcfg =  {   tag: "div",
37426                             cls: "roo-layout-inactive-content",
37427                             id: config.id||el
37428                             };
37429             if (config.html) {
37430                 elcfg.html = config.html;
37431                 
37432             }
37433                         
37434             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37435         }
37436     } 
37437     this.closable = false;
37438     this.loaded = false;
37439     this.active = false;
37440    
37441       
37442     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37443         
37444         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37445         
37446         this.wrapEl = this.el; //this.el.wrap();
37447         var ti = [];
37448         if (config.toolbar.items) {
37449             ti = config.toolbar.items ;
37450             delete config.toolbar.items ;
37451         }
37452         
37453         var nitems = [];
37454         this.toolbar.render(this.wrapEl, 'before');
37455         for(var i =0;i < ti.length;i++) {
37456           //  Roo.log(['add child', items[i]]);
37457             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37458         }
37459         this.toolbar.items = nitems;
37460         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37461         delete config.toolbar;
37462         
37463     }
37464     /*
37465     // xtype created footer. - not sure if will work as we normally have to render first..
37466     if (this.footer && !this.footer.el && this.footer.xtype) {
37467         if (!this.wrapEl) {
37468             this.wrapEl = this.el.wrap();
37469         }
37470     
37471         this.footer.container = this.wrapEl.createChild();
37472          
37473         this.footer = Roo.factory(this.footer, Roo);
37474         
37475     }
37476     */
37477     
37478      if(typeof config == "string"){
37479         this.title = config;
37480     }else{
37481         Roo.apply(this, config);
37482     }
37483     
37484     if(this.resizeEl){
37485         this.resizeEl = Roo.get(this.resizeEl, true);
37486     }else{
37487         this.resizeEl = this.el;
37488     }
37489     // handle view.xtype
37490     
37491  
37492     
37493     
37494     this.addEvents({
37495         /**
37496          * @event activate
37497          * Fires when this panel is activated. 
37498          * @param {Roo.ContentPanel} this
37499          */
37500         "activate" : true,
37501         /**
37502          * @event deactivate
37503          * Fires when this panel is activated. 
37504          * @param {Roo.ContentPanel} this
37505          */
37506         "deactivate" : true,
37507
37508         /**
37509          * @event resize
37510          * Fires when this panel is resized if fitToFrame is true.
37511          * @param {Roo.ContentPanel} this
37512          * @param {Number} width The width after any component adjustments
37513          * @param {Number} height The height after any component adjustments
37514          */
37515         "resize" : true,
37516         
37517          /**
37518          * @event render
37519          * Fires when this tab is created
37520          * @param {Roo.ContentPanel} this
37521          */
37522         "render" : true
37523         
37524         
37525         
37526     });
37527     
37528
37529     
37530     
37531     if(this.autoScroll){
37532         this.resizeEl.setStyle("overflow", "auto");
37533     } else {
37534         // fix randome scrolling
37535         //this.el.on('scroll', function() {
37536         //    Roo.log('fix random scolling');
37537         //    this.scrollTo('top',0); 
37538         //});
37539     }
37540     content = content || this.content;
37541     if(content){
37542         this.setContent(content);
37543     }
37544     if(config && config.url){
37545         this.setUrl(this.url, this.params, this.loadOnce);
37546     }
37547     
37548     
37549     
37550     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37551     
37552     if (this.view && typeof(this.view.xtype) != 'undefined') {
37553         this.view.el = this.el.appendChild(document.createElement("div"));
37554         this.view = Roo.factory(this.view); 
37555         this.view.render  &&  this.view.render(false, '');  
37556     }
37557     
37558     
37559     this.fireEvent('render', this);
37560 };
37561
37562 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37563     
37564     tabTip : '',
37565     
37566     setRegion : function(region){
37567         this.region = region;
37568         this.setActiveClass(region && !this.background);
37569     },
37570     
37571     
37572     setActiveClass: function(state)
37573     {
37574         if(state){
37575            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37576            this.el.setStyle('position','relative');
37577         }else{
37578            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37579            this.el.setStyle('position', 'absolute');
37580         } 
37581     },
37582     
37583     /**
37584      * Returns the toolbar for this Panel if one was configured. 
37585      * @return {Roo.Toolbar} 
37586      */
37587     getToolbar : function(){
37588         return this.toolbar;
37589     },
37590     
37591     setActiveState : function(active)
37592     {
37593         this.active = active;
37594         this.setActiveClass(active);
37595         if(!active){
37596             if(this.fireEvent("deactivate", this) === false){
37597                 return false;
37598             }
37599             return true;
37600         }
37601         this.fireEvent("activate", this);
37602         return true;
37603     },
37604     /**
37605      * Updates this panel's element
37606      * @param {String} content The new content
37607      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37608     */
37609     setContent : function(content, loadScripts){
37610         this.el.update(content, loadScripts);
37611     },
37612
37613     ignoreResize : function(w, h){
37614         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37615             return true;
37616         }else{
37617             this.lastSize = {width: w, height: h};
37618             return false;
37619         }
37620     },
37621     /**
37622      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37623      * @return {Roo.UpdateManager} The UpdateManager
37624      */
37625     getUpdateManager : function(){
37626         return this.el.getUpdateManager();
37627     },
37628      /**
37629      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37630      * @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:
37631 <pre><code>
37632 panel.load({
37633     url: "your-url.php",
37634     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37635     callback: yourFunction,
37636     scope: yourObject, //(optional scope)
37637     discardUrl: false,
37638     nocache: false,
37639     text: "Loading...",
37640     timeout: 30,
37641     scripts: false
37642 });
37643 </code></pre>
37644      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37645      * 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.
37646      * @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}
37647      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37648      * @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.
37649      * @return {Roo.ContentPanel} this
37650      */
37651     load : function(){
37652         var um = this.el.getUpdateManager();
37653         um.update.apply(um, arguments);
37654         return this;
37655     },
37656
37657
37658     /**
37659      * 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.
37660      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37661      * @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)
37662      * @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)
37663      * @return {Roo.UpdateManager} The UpdateManager
37664      */
37665     setUrl : function(url, params, loadOnce){
37666         if(this.refreshDelegate){
37667             this.removeListener("activate", this.refreshDelegate);
37668         }
37669         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37670         this.on("activate", this.refreshDelegate);
37671         return this.el.getUpdateManager();
37672     },
37673     
37674     _handleRefresh : function(url, params, loadOnce){
37675         if(!loadOnce || !this.loaded){
37676             var updater = this.el.getUpdateManager();
37677             updater.update(url, params, this._setLoaded.createDelegate(this));
37678         }
37679     },
37680     
37681     _setLoaded : function(){
37682         this.loaded = true;
37683     }, 
37684     
37685     /**
37686      * Returns this panel's id
37687      * @return {String} 
37688      */
37689     getId : function(){
37690         return this.el.id;
37691     },
37692     
37693     /** 
37694      * Returns this panel's element - used by regiosn to add.
37695      * @return {Roo.Element} 
37696      */
37697     getEl : function(){
37698         return this.wrapEl || this.el;
37699     },
37700     
37701    
37702     
37703     adjustForComponents : function(width, height)
37704     {
37705         //Roo.log('adjustForComponents ');
37706         if(this.resizeEl != this.el){
37707             width -= this.el.getFrameWidth('lr');
37708             height -= this.el.getFrameWidth('tb');
37709         }
37710         if(this.toolbar){
37711             var te = this.toolbar.getEl();
37712             te.setWidth(width);
37713             height -= te.getHeight();
37714         }
37715         if(this.footer){
37716             var te = this.footer.getEl();
37717             te.setWidth(width);
37718             height -= te.getHeight();
37719         }
37720         
37721         
37722         if(this.adjustments){
37723             width += this.adjustments[0];
37724             height += this.adjustments[1];
37725         }
37726         return {"width": width, "height": height};
37727     },
37728     
37729     setSize : function(width, height){
37730         if(this.fitToFrame && !this.ignoreResize(width, height)){
37731             if(this.fitContainer && this.resizeEl != this.el){
37732                 this.el.setSize(width, height);
37733             }
37734             var size = this.adjustForComponents(width, height);
37735             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37736             this.fireEvent('resize', this, size.width, size.height);
37737         }
37738     },
37739     
37740     /**
37741      * Returns this panel's title
37742      * @return {String} 
37743      */
37744     getTitle : function(){
37745         
37746         if (typeof(this.title) != 'object') {
37747             return this.title;
37748         }
37749         
37750         var t = '';
37751         for (var k in this.title) {
37752             if (!this.title.hasOwnProperty(k)) {
37753                 continue;
37754             }
37755             
37756             if (k.indexOf('-') >= 0) {
37757                 var s = k.split('-');
37758                 for (var i = 0; i<s.length; i++) {
37759                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37760                 }
37761             } else {
37762                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37763             }
37764         }
37765         return t;
37766     },
37767     
37768     /**
37769      * Set this panel's title
37770      * @param {String} title
37771      */
37772     setTitle : function(title){
37773         this.title = title;
37774         if(this.region){
37775             this.region.updatePanelTitle(this, title);
37776         }
37777     },
37778     
37779     /**
37780      * Returns true is this panel was configured to be closable
37781      * @return {Boolean} 
37782      */
37783     isClosable : function(){
37784         return this.closable;
37785     },
37786     
37787     beforeSlide : function(){
37788         this.el.clip();
37789         this.resizeEl.clip();
37790     },
37791     
37792     afterSlide : function(){
37793         this.el.unclip();
37794         this.resizeEl.unclip();
37795     },
37796     
37797     /**
37798      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37799      *   Will fail silently if the {@link #setUrl} method has not been called.
37800      *   This does not activate the panel, just updates its content.
37801      */
37802     refresh : function(){
37803         if(this.refreshDelegate){
37804            this.loaded = false;
37805            this.refreshDelegate();
37806         }
37807     },
37808     
37809     /**
37810      * Destroys this panel
37811      */
37812     destroy : function(){
37813         this.el.removeAllListeners();
37814         var tempEl = document.createElement("span");
37815         tempEl.appendChild(this.el.dom);
37816         tempEl.innerHTML = "";
37817         this.el.remove();
37818         this.el = null;
37819     },
37820     
37821     /**
37822      * form - if the content panel contains a form - this is a reference to it.
37823      * @type {Roo.form.Form}
37824      */
37825     form : false,
37826     /**
37827      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37828      *    This contains a reference to it.
37829      * @type {Roo.View}
37830      */
37831     view : false,
37832     
37833       /**
37834      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37835      * <pre><code>
37836
37837 layout.addxtype({
37838        xtype : 'Form',
37839        items: [ .... ]
37840    }
37841 );
37842
37843 </code></pre>
37844      * @param {Object} cfg Xtype definition of item to add.
37845      */
37846     
37847     
37848     getChildContainer: function () {
37849         return this.getEl();
37850     }
37851     
37852     
37853     /*
37854         var  ret = new Roo.factory(cfg);
37855         return ret;
37856         
37857         
37858         // add form..
37859         if (cfg.xtype.match(/^Form$/)) {
37860             
37861             var el;
37862             //if (this.footer) {
37863             //    el = this.footer.container.insertSibling(false, 'before');
37864             //} else {
37865                 el = this.el.createChild();
37866             //}
37867
37868             this.form = new  Roo.form.Form(cfg);
37869             
37870             
37871             if ( this.form.allItems.length) {
37872                 this.form.render(el.dom);
37873             }
37874             return this.form;
37875         }
37876         // should only have one of theses..
37877         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37878             // views.. should not be just added - used named prop 'view''
37879             
37880             cfg.el = this.el.appendChild(document.createElement("div"));
37881             // factory?
37882             
37883             var ret = new Roo.factory(cfg);
37884              
37885              ret.render && ret.render(false, ''); // render blank..
37886             this.view = ret;
37887             return ret;
37888         }
37889         return false;
37890     }
37891     \*/
37892 });
37893  
37894 /**
37895  * @class Roo.bootstrap.panel.Grid
37896  * @extends Roo.bootstrap.panel.Content
37897  * @constructor
37898  * Create a new GridPanel.
37899  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37900  * @param {Object} config A the config object
37901   
37902  */
37903
37904
37905
37906 Roo.bootstrap.panel.Grid = function(config)
37907 {
37908     
37909       
37910     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37911         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37912
37913     config.el = this.wrapper;
37914     //this.el = this.wrapper;
37915     
37916       if (config.container) {
37917         // ctor'ed from a Border/panel.grid
37918         
37919         
37920         this.wrapper.setStyle("overflow", "hidden");
37921         this.wrapper.addClass('roo-grid-container');
37922
37923     }
37924     
37925     
37926     if(config.toolbar){
37927         var tool_el = this.wrapper.createChild();    
37928         this.toolbar = Roo.factory(config.toolbar);
37929         var ti = [];
37930         if (config.toolbar.items) {
37931             ti = config.toolbar.items ;
37932             delete config.toolbar.items ;
37933         }
37934         
37935         var nitems = [];
37936         this.toolbar.render(tool_el);
37937         for(var i =0;i < ti.length;i++) {
37938           //  Roo.log(['add child', items[i]]);
37939             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37940         }
37941         this.toolbar.items = nitems;
37942         
37943         delete config.toolbar;
37944     }
37945     
37946     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37947     config.grid.scrollBody = true;;
37948     config.grid.monitorWindowResize = false; // turn off autosizing
37949     config.grid.autoHeight = false;
37950     config.grid.autoWidth = false;
37951     
37952     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37953     
37954     if (config.background) {
37955         // render grid on panel activation (if panel background)
37956         this.on('activate', function(gp) {
37957             if (!gp.grid.rendered) {
37958                 gp.grid.render(this.wrapper);
37959                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37960             }
37961         });
37962             
37963     } else {
37964         this.grid.render(this.wrapper);
37965         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37966
37967     }
37968     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37969     // ??? needed ??? config.el = this.wrapper;
37970     
37971     
37972     
37973   
37974     // xtype created footer. - not sure if will work as we normally have to render first..
37975     if (this.footer && !this.footer.el && this.footer.xtype) {
37976         
37977         var ctr = this.grid.getView().getFooterPanel(true);
37978         this.footer.dataSource = this.grid.dataSource;
37979         this.footer = Roo.factory(this.footer, Roo);
37980         this.footer.render(ctr);
37981         
37982     }
37983     
37984     
37985     
37986     
37987      
37988 };
37989
37990 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37991     getId : function(){
37992         return this.grid.id;
37993     },
37994     
37995     /**
37996      * Returns the grid for this panel
37997      * @return {Roo.bootstrap.Table} 
37998      */
37999     getGrid : function(){
38000         return this.grid;    
38001     },
38002     
38003     setSize : function(width, height){
38004         if(!this.ignoreResize(width, height)){
38005             var grid = this.grid;
38006             var size = this.adjustForComponents(width, height);
38007             var gridel = grid.getGridEl();
38008             gridel.setSize(size.width, size.height);
38009             /*
38010             var thd = grid.getGridEl().select('thead',true).first();
38011             var tbd = grid.getGridEl().select('tbody', true).first();
38012             if (tbd) {
38013                 tbd.setSize(width, height - thd.getHeight());
38014             }
38015             */
38016             grid.autoSize();
38017         }
38018     },
38019      
38020     
38021     
38022     beforeSlide : function(){
38023         this.grid.getView().scroller.clip();
38024     },
38025     
38026     afterSlide : function(){
38027         this.grid.getView().scroller.unclip();
38028     },
38029     
38030     destroy : function(){
38031         this.grid.destroy();
38032         delete this.grid;
38033         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
38034     }
38035 });
38036
38037 /**
38038  * @class Roo.bootstrap.panel.Nest
38039  * @extends Roo.bootstrap.panel.Content
38040  * @constructor
38041  * Create a new Panel, that can contain a layout.Border.
38042  * 
38043  * 
38044  * @param {Roo.BorderLayout} layout The layout for this panel
38045  * @param {String/Object} config A string to set only the title or a config object
38046  */
38047 Roo.bootstrap.panel.Nest = function(config)
38048 {
38049     // construct with only one argument..
38050     /* FIXME - implement nicer consturctors
38051     if (layout.layout) {
38052         config = layout;
38053         layout = config.layout;
38054         delete config.layout;
38055     }
38056     if (layout.xtype && !layout.getEl) {
38057         // then layout needs constructing..
38058         layout = Roo.factory(layout, Roo);
38059     }
38060     */
38061     
38062     config.el =  config.layout.getEl();
38063     
38064     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
38065     
38066     config.layout.monitorWindowResize = false; // turn off autosizing
38067     this.layout = config.layout;
38068     this.layout.getEl().addClass("roo-layout-nested-layout");
38069     this.layout.parent = this;
38070     
38071     
38072     
38073     
38074 };
38075
38076 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
38077
38078     setSize : function(width, height){
38079         if(!this.ignoreResize(width, height)){
38080             var size = this.adjustForComponents(width, height);
38081             var el = this.layout.getEl();
38082             if (size.height < 1) {
38083                 el.setWidth(size.width);   
38084             } else {
38085                 el.setSize(size.width, size.height);
38086             }
38087             var touch = el.dom.offsetWidth;
38088             this.layout.layout();
38089             // ie requires a double layout on the first pass
38090             if(Roo.isIE && !this.initialized){
38091                 this.initialized = true;
38092                 this.layout.layout();
38093             }
38094         }
38095     },
38096     
38097     // activate all subpanels if not currently active..
38098     
38099     setActiveState : function(active){
38100         this.active = active;
38101         this.setActiveClass(active);
38102         
38103         if(!active){
38104             this.fireEvent("deactivate", this);
38105             return;
38106         }
38107         
38108         this.fireEvent("activate", this);
38109         // not sure if this should happen before or after..
38110         if (!this.layout) {
38111             return; // should not happen..
38112         }
38113         var reg = false;
38114         for (var r in this.layout.regions) {
38115             reg = this.layout.getRegion(r);
38116             if (reg.getActivePanel()) {
38117                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
38118                 reg.setActivePanel(reg.getActivePanel());
38119                 continue;
38120             }
38121             if (!reg.panels.length) {
38122                 continue;
38123             }
38124             reg.showPanel(reg.getPanel(0));
38125         }
38126         
38127         
38128         
38129         
38130     },
38131     
38132     /**
38133      * Returns the nested BorderLayout for this panel
38134      * @return {Roo.BorderLayout} 
38135      */
38136     getLayout : function(){
38137         return this.layout;
38138     },
38139     
38140      /**
38141      * Adds a xtype elements to the layout of the nested panel
38142      * <pre><code>
38143
38144 panel.addxtype({
38145        xtype : 'ContentPanel',
38146        region: 'west',
38147        items: [ .... ]
38148    }
38149 );
38150
38151 panel.addxtype({
38152         xtype : 'NestedLayoutPanel',
38153         region: 'west',
38154         layout: {
38155            center: { },
38156            west: { }   
38157         },
38158         items : [ ... list of content panels or nested layout panels.. ]
38159    }
38160 );
38161 </code></pre>
38162      * @param {Object} cfg Xtype definition of item to add.
38163      */
38164     addxtype : function(cfg) {
38165         return this.layout.addxtype(cfg);
38166     
38167     }
38168 });/*
38169  * Based on:
38170  * Ext JS Library 1.1.1
38171  * Copyright(c) 2006-2007, Ext JS, LLC.
38172  *
38173  * Originally Released Under LGPL - original licence link has changed is not relivant.
38174  *
38175  * Fork - LGPL
38176  * <script type="text/javascript">
38177  */
38178 /**
38179  * @class Roo.TabPanel
38180  * @extends Roo.util.Observable
38181  * A lightweight tab container.
38182  * <br><br>
38183  * Usage:
38184  * <pre><code>
38185 // basic tabs 1, built from existing content
38186 var tabs = new Roo.TabPanel("tabs1");
38187 tabs.addTab("script", "View Script");
38188 tabs.addTab("markup", "View Markup");
38189 tabs.activate("script");
38190
38191 // more advanced tabs, built from javascript
38192 var jtabs = new Roo.TabPanel("jtabs");
38193 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38194
38195 // set up the UpdateManager
38196 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38197 var updater = tab2.getUpdateManager();
38198 updater.setDefaultUrl("ajax1.htm");
38199 tab2.on('activate', updater.refresh, updater, true);
38200
38201 // Use setUrl for Ajax loading
38202 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38203 tab3.setUrl("ajax2.htm", null, true);
38204
38205 // Disabled tab
38206 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38207 tab4.disable();
38208
38209 jtabs.activate("jtabs-1");
38210  * </code></pre>
38211  * @constructor
38212  * Create a new TabPanel.
38213  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38214  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38215  */
38216 Roo.bootstrap.panel.Tabs = function(config){
38217     /**
38218     * The container element for this TabPanel.
38219     * @type Roo.Element
38220     */
38221     this.el = Roo.get(config.el);
38222     delete config.el;
38223     if(config){
38224         if(typeof config == "boolean"){
38225             this.tabPosition = config ? "bottom" : "top";
38226         }else{
38227             Roo.apply(this, config);
38228         }
38229     }
38230     
38231     if(this.tabPosition == "bottom"){
38232         // if tabs are at the bottom = create the body first.
38233         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38234         this.el.addClass("roo-tabs-bottom");
38235     }
38236     // next create the tabs holders
38237     
38238     if (this.tabPosition == "west"){
38239         
38240         var reg = this.region; // fake it..
38241         while (reg) {
38242             if (!reg.mgr.parent) {
38243                 break;
38244             }
38245             reg = reg.mgr.parent.region;
38246         }
38247         Roo.log("got nest?");
38248         Roo.log(reg);
38249         if (reg.mgr.getRegion('west')) {
38250             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38251             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38252             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38253             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38254             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38255         
38256             
38257         }
38258         
38259         
38260     } else {
38261      
38262         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38263         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38264         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38265         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38266     }
38267     
38268     
38269     if(Roo.isIE){
38270         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38271     }
38272     
38273     // finally - if tabs are at the top, then create the body last..
38274     if(this.tabPosition != "bottom"){
38275         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38276          * @type Roo.Element
38277          */
38278         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38279         this.el.addClass("roo-tabs-top");
38280     }
38281     this.items = [];
38282
38283     this.bodyEl.setStyle("position", "relative");
38284
38285     this.active = null;
38286     this.activateDelegate = this.activate.createDelegate(this);
38287
38288     this.addEvents({
38289         /**
38290          * @event tabchange
38291          * Fires when the active tab changes
38292          * @param {Roo.TabPanel} this
38293          * @param {Roo.TabPanelItem} activePanel The new active tab
38294          */
38295         "tabchange": true,
38296         /**
38297          * @event beforetabchange
38298          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38299          * @param {Roo.TabPanel} this
38300          * @param {Object} e Set cancel to true on this object to cancel the tab change
38301          * @param {Roo.TabPanelItem} tab The tab being changed to
38302          */
38303         "beforetabchange" : true
38304     });
38305
38306     Roo.EventManager.onWindowResize(this.onResize, this);
38307     this.cpad = this.el.getPadding("lr");
38308     this.hiddenCount = 0;
38309
38310
38311     // toolbar on the tabbar support...
38312     if (this.toolbar) {
38313         alert("no toolbar support yet");
38314         this.toolbar  = false;
38315         /*
38316         var tcfg = this.toolbar;
38317         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38318         this.toolbar = new Roo.Toolbar(tcfg);
38319         if (Roo.isSafari) {
38320             var tbl = tcfg.container.child('table', true);
38321             tbl.setAttribute('width', '100%');
38322         }
38323         */
38324         
38325     }
38326    
38327
38328
38329     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38330 };
38331
38332 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38333     /*
38334      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38335      */
38336     tabPosition : "top",
38337     /*
38338      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38339      */
38340     currentTabWidth : 0,
38341     /*
38342      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38343      */
38344     minTabWidth : 40,
38345     /*
38346      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38347      */
38348     maxTabWidth : 250,
38349     /*
38350      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38351      */
38352     preferredTabWidth : 175,
38353     /*
38354      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38355      */
38356     resizeTabs : false,
38357     /*
38358      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38359      */
38360     monitorResize : true,
38361     /*
38362      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38363      */
38364     toolbar : false,  // set by caller..
38365     
38366     region : false, /// set by caller
38367     
38368     disableTooltips : true, // not used yet...
38369
38370     /**
38371      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38372      * @param {String} id The id of the div to use <b>or create</b>
38373      * @param {String} text The text for the tab
38374      * @param {String} content (optional) Content to put in the TabPanelItem body
38375      * @param {Boolean} closable (optional) True to create a close icon on the tab
38376      * @return {Roo.TabPanelItem} The created TabPanelItem
38377      */
38378     addTab : function(id, text, content, closable, tpl)
38379     {
38380         var item = new Roo.bootstrap.panel.TabItem({
38381             panel: this,
38382             id : id,
38383             text : text,
38384             closable : closable,
38385             tpl : tpl
38386         });
38387         this.addTabItem(item);
38388         if(content){
38389             item.setContent(content);
38390         }
38391         return item;
38392     },
38393
38394     /**
38395      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38396      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38397      * @return {Roo.TabPanelItem}
38398      */
38399     getTab : function(id){
38400         return this.items[id];
38401     },
38402
38403     /**
38404      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38405      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38406      */
38407     hideTab : function(id){
38408         var t = this.items[id];
38409         if(!t.isHidden()){
38410            t.setHidden(true);
38411            this.hiddenCount++;
38412            this.autoSizeTabs();
38413         }
38414     },
38415
38416     /**
38417      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38418      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38419      */
38420     unhideTab : function(id){
38421         var t = this.items[id];
38422         if(t.isHidden()){
38423            t.setHidden(false);
38424            this.hiddenCount--;
38425            this.autoSizeTabs();
38426         }
38427     },
38428
38429     /**
38430      * Adds an existing {@link Roo.TabPanelItem}.
38431      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38432      */
38433     addTabItem : function(item)
38434     {
38435         this.items[item.id] = item;
38436         this.items.push(item);
38437         this.autoSizeTabs();
38438       //  if(this.resizeTabs){
38439     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38440   //         this.autoSizeTabs();
38441 //        }else{
38442 //            item.autoSize();
38443        // }
38444     },
38445
38446     /**
38447      * Removes a {@link Roo.TabPanelItem}.
38448      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38449      */
38450     removeTab : function(id){
38451         var items = this.items;
38452         var tab = items[id];
38453         if(!tab) { return; }
38454         var index = items.indexOf(tab);
38455         if(this.active == tab && items.length > 1){
38456             var newTab = this.getNextAvailable(index);
38457             if(newTab) {
38458                 newTab.activate();
38459             }
38460         }
38461         this.stripEl.dom.removeChild(tab.pnode.dom);
38462         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38463             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38464         }
38465         items.splice(index, 1);
38466         delete this.items[tab.id];
38467         tab.fireEvent("close", tab);
38468         tab.purgeListeners();
38469         this.autoSizeTabs();
38470     },
38471
38472     getNextAvailable : function(start){
38473         var items = this.items;
38474         var index = start;
38475         // look for a next tab that will slide over to
38476         // replace the one being removed
38477         while(index < items.length){
38478             var item = items[++index];
38479             if(item && !item.isHidden()){
38480                 return item;
38481             }
38482         }
38483         // if one isn't found select the previous tab (on the left)
38484         index = start;
38485         while(index >= 0){
38486             var item = items[--index];
38487             if(item && !item.isHidden()){
38488                 return item;
38489             }
38490         }
38491         return null;
38492     },
38493
38494     /**
38495      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38496      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38497      */
38498     disableTab : function(id){
38499         var tab = this.items[id];
38500         if(tab && this.active != tab){
38501             tab.disable();
38502         }
38503     },
38504
38505     /**
38506      * Enables a {@link Roo.TabPanelItem} that is disabled.
38507      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38508      */
38509     enableTab : function(id){
38510         var tab = this.items[id];
38511         tab.enable();
38512     },
38513
38514     /**
38515      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38516      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38517      * @return {Roo.TabPanelItem} The TabPanelItem.
38518      */
38519     activate : function(id)
38520     {
38521         //Roo.log('activite:'  + id);
38522         
38523         var tab = this.items[id];
38524         if(!tab){
38525             return null;
38526         }
38527         if(tab == this.active || tab.disabled){
38528             return tab;
38529         }
38530         var e = {};
38531         this.fireEvent("beforetabchange", this, e, tab);
38532         if(e.cancel !== true && !tab.disabled){
38533             if(this.active){
38534                 this.active.hide();
38535             }
38536             this.active = this.items[id];
38537             this.active.show();
38538             this.fireEvent("tabchange", this, this.active);
38539         }
38540         return tab;
38541     },
38542
38543     /**
38544      * Gets the active {@link Roo.TabPanelItem}.
38545      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38546      */
38547     getActiveTab : function(){
38548         return this.active;
38549     },
38550
38551     /**
38552      * Updates the tab body element to fit the height of the container element
38553      * for overflow scrolling
38554      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38555      */
38556     syncHeight : function(targetHeight){
38557         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38558         var bm = this.bodyEl.getMargins();
38559         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38560         this.bodyEl.setHeight(newHeight);
38561         return newHeight;
38562     },
38563
38564     onResize : function(){
38565         if(this.monitorResize){
38566             this.autoSizeTabs();
38567         }
38568     },
38569
38570     /**
38571      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38572      */
38573     beginUpdate : function(){
38574         this.updating = true;
38575     },
38576
38577     /**
38578      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38579      */
38580     endUpdate : function(){
38581         this.updating = false;
38582         this.autoSizeTabs();
38583     },
38584
38585     /**
38586      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38587      */
38588     autoSizeTabs : function()
38589     {
38590         var count = this.items.length;
38591         var vcount = count - this.hiddenCount;
38592         
38593         if (vcount < 2) {
38594             this.stripEl.hide();
38595         } else {
38596             this.stripEl.show();
38597         }
38598         
38599         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38600             return;
38601         }
38602         
38603         
38604         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38605         var availWidth = Math.floor(w / vcount);
38606         var b = this.stripBody;
38607         if(b.getWidth() > w){
38608             var tabs = this.items;
38609             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38610             if(availWidth < this.minTabWidth){
38611                 /*if(!this.sleft){    // incomplete scrolling code
38612                     this.createScrollButtons();
38613                 }
38614                 this.showScroll();
38615                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38616             }
38617         }else{
38618             if(this.currentTabWidth < this.preferredTabWidth){
38619                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38620             }
38621         }
38622     },
38623
38624     /**
38625      * Returns the number of tabs in this TabPanel.
38626      * @return {Number}
38627      */
38628      getCount : function(){
38629          return this.items.length;
38630      },
38631
38632     /**
38633      * Resizes all the tabs to the passed width
38634      * @param {Number} The new width
38635      */
38636     setTabWidth : function(width){
38637         this.currentTabWidth = width;
38638         for(var i = 0, len = this.items.length; i < len; i++) {
38639                 if(!this.items[i].isHidden()) {
38640                 this.items[i].setWidth(width);
38641             }
38642         }
38643     },
38644
38645     /**
38646      * Destroys this TabPanel
38647      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38648      */
38649     destroy : function(removeEl){
38650         Roo.EventManager.removeResizeListener(this.onResize, this);
38651         for(var i = 0, len = this.items.length; i < len; i++){
38652             this.items[i].purgeListeners();
38653         }
38654         if(removeEl === true){
38655             this.el.update("");
38656             this.el.remove();
38657         }
38658     },
38659     
38660     createStrip : function(container)
38661     {
38662         var strip = document.createElement("nav");
38663         strip.className = Roo.bootstrap.version == 4 ?
38664             "navbar-light bg-light" : 
38665             "navbar navbar-default"; //"x-tabs-wrap";
38666         container.appendChild(strip);
38667         return strip;
38668     },
38669     
38670     createStripList : function(strip)
38671     {
38672         // div wrapper for retard IE
38673         // returns the "tr" element.
38674         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38675         //'<div class="x-tabs-strip-wrap">'+
38676           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38677           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38678         return strip.firstChild; //.firstChild.firstChild.firstChild;
38679     },
38680     createBody : function(container)
38681     {
38682         var body = document.createElement("div");
38683         Roo.id(body, "tab-body");
38684         //Roo.fly(body).addClass("x-tabs-body");
38685         Roo.fly(body).addClass("tab-content");
38686         container.appendChild(body);
38687         return body;
38688     },
38689     createItemBody :function(bodyEl, id){
38690         var body = Roo.getDom(id);
38691         if(!body){
38692             body = document.createElement("div");
38693             body.id = id;
38694         }
38695         //Roo.fly(body).addClass("x-tabs-item-body");
38696         Roo.fly(body).addClass("tab-pane");
38697          bodyEl.insertBefore(body, bodyEl.firstChild);
38698         return body;
38699     },
38700     /** @private */
38701     createStripElements :  function(stripEl, text, closable, tpl)
38702     {
38703         var td = document.createElement("li"); // was td..
38704         td.className = 'nav-item';
38705         
38706         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38707         
38708         
38709         stripEl.appendChild(td);
38710         /*if(closable){
38711             td.className = "x-tabs-closable";
38712             if(!this.closeTpl){
38713                 this.closeTpl = new Roo.Template(
38714                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38715                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38716                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38717                 );
38718             }
38719             var el = this.closeTpl.overwrite(td, {"text": text});
38720             var close = el.getElementsByTagName("div")[0];
38721             var inner = el.getElementsByTagName("em")[0];
38722             return {"el": el, "close": close, "inner": inner};
38723         } else {
38724         */
38725         // not sure what this is..
38726 //            if(!this.tabTpl){
38727                 //this.tabTpl = new Roo.Template(
38728                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38729                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38730                 //);
38731 //                this.tabTpl = new Roo.Template(
38732 //                   '<a href="#">' +
38733 //                   '<span unselectable="on"' +
38734 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38735 //                            ' >{text}</span></a>'
38736 //                );
38737 //                
38738 //            }
38739
38740
38741             var template = tpl || this.tabTpl || false;
38742             
38743             if(!template){
38744                 template =  new Roo.Template(
38745                         Roo.bootstrap.version == 4 ? 
38746                             (
38747                                 '<a class="nav-link" href="#" unselectable="on"' +
38748                                      (this.disableTooltips ? '' : ' title="{text}"') +
38749                                      ' >{text}</a>'
38750                             ) : (
38751                                 '<a class="nav-link" href="#">' +
38752                                 '<span unselectable="on"' +
38753                                          (this.disableTooltips ? '' : ' title="{text}"') +
38754                                     ' >{text}</span></a>'
38755                             )
38756                 );
38757             }
38758             
38759             switch (typeof(template)) {
38760                 case 'object' :
38761                     break;
38762                 case 'string' :
38763                     template = new Roo.Template(template);
38764                     break;
38765                 default :
38766                     break;
38767             }
38768             
38769             var el = template.overwrite(td, {"text": text});
38770             
38771             var inner = el.getElementsByTagName("span")[0];
38772             
38773             return {"el": el, "inner": inner};
38774             
38775     }
38776         
38777     
38778 });
38779
38780 /**
38781  * @class Roo.TabPanelItem
38782  * @extends Roo.util.Observable
38783  * Represents an individual item (tab plus body) in a TabPanel.
38784  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38785  * @param {String} id The id of this TabPanelItem
38786  * @param {String} text The text for the tab of this TabPanelItem
38787  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38788  */
38789 Roo.bootstrap.panel.TabItem = function(config){
38790     /**
38791      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38792      * @type Roo.TabPanel
38793      */
38794     this.tabPanel = config.panel;
38795     /**
38796      * The id for this TabPanelItem
38797      * @type String
38798      */
38799     this.id = config.id;
38800     /** @private */
38801     this.disabled = false;
38802     /** @private */
38803     this.text = config.text;
38804     /** @private */
38805     this.loaded = false;
38806     this.closable = config.closable;
38807
38808     /**
38809      * The body element for this TabPanelItem.
38810      * @type Roo.Element
38811      */
38812     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38813     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38814     this.bodyEl.setStyle("display", "block");
38815     this.bodyEl.setStyle("zoom", "1");
38816     //this.hideAction();
38817
38818     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38819     /** @private */
38820     this.el = Roo.get(els.el);
38821     this.inner = Roo.get(els.inner, true);
38822      this.textEl = Roo.bootstrap.version == 4 ?
38823         this.el : Roo.get(this.el.dom.firstChild, true);
38824
38825     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38826     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38827
38828     
38829 //    this.el.on("mousedown", this.onTabMouseDown, this);
38830     this.el.on("click", this.onTabClick, this);
38831     /** @private */
38832     if(config.closable){
38833         var c = Roo.get(els.close, true);
38834         c.dom.title = this.closeText;
38835         c.addClassOnOver("close-over");
38836         c.on("click", this.closeClick, this);
38837      }
38838
38839     this.addEvents({
38840          /**
38841          * @event activate
38842          * Fires when this tab becomes the active tab.
38843          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38844          * @param {Roo.TabPanelItem} this
38845          */
38846         "activate": true,
38847         /**
38848          * @event beforeclose
38849          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38850          * @param {Roo.TabPanelItem} this
38851          * @param {Object} e Set cancel to true on this object to cancel the close.
38852          */
38853         "beforeclose": true,
38854         /**
38855          * @event close
38856          * Fires when this tab is closed.
38857          * @param {Roo.TabPanelItem} this
38858          */
38859          "close": true,
38860         /**
38861          * @event deactivate
38862          * Fires when this tab is no longer the active tab.
38863          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38864          * @param {Roo.TabPanelItem} this
38865          */
38866          "deactivate" : true
38867     });
38868     this.hidden = false;
38869
38870     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38871 };
38872
38873 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38874            {
38875     purgeListeners : function(){
38876        Roo.util.Observable.prototype.purgeListeners.call(this);
38877        this.el.removeAllListeners();
38878     },
38879     /**
38880      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38881      */
38882     show : function(){
38883         this.status_node.addClass("active");
38884         this.showAction();
38885         if(Roo.isOpera){
38886             this.tabPanel.stripWrap.repaint();
38887         }
38888         this.fireEvent("activate", this.tabPanel, this);
38889     },
38890
38891     /**
38892      * Returns true if this tab is the active tab.
38893      * @return {Boolean}
38894      */
38895     isActive : function(){
38896         return this.tabPanel.getActiveTab() == this;
38897     },
38898
38899     /**
38900      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38901      */
38902     hide : function(){
38903         this.status_node.removeClass("active");
38904         this.hideAction();
38905         this.fireEvent("deactivate", this.tabPanel, this);
38906     },
38907
38908     hideAction : function(){
38909         this.bodyEl.hide();
38910         this.bodyEl.setStyle("position", "absolute");
38911         this.bodyEl.setLeft("-20000px");
38912         this.bodyEl.setTop("-20000px");
38913     },
38914
38915     showAction : function(){
38916         this.bodyEl.setStyle("position", "relative");
38917         this.bodyEl.setTop("");
38918         this.bodyEl.setLeft("");
38919         this.bodyEl.show();
38920     },
38921
38922     /**
38923      * Set the tooltip for the tab.
38924      * @param {String} tooltip The tab's tooltip
38925      */
38926     setTooltip : function(text){
38927         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38928             this.textEl.dom.qtip = text;
38929             this.textEl.dom.removeAttribute('title');
38930         }else{
38931             this.textEl.dom.title = text;
38932         }
38933     },
38934
38935     onTabClick : function(e){
38936         e.preventDefault();
38937         this.tabPanel.activate(this.id);
38938     },
38939
38940     onTabMouseDown : function(e){
38941         e.preventDefault();
38942         this.tabPanel.activate(this.id);
38943     },
38944 /*
38945     getWidth : function(){
38946         return this.inner.getWidth();
38947     },
38948
38949     setWidth : function(width){
38950         var iwidth = width - this.linode.getPadding("lr");
38951         this.inner.setWidth(iwidth);
38952         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38953         this.linode.setWidth(width);
38954     },
38955 */
38956     /**
38957      * Show or hide the tab
38958      * @param {Boolean} hidden True to hide or false to show.
38959      */
38960     setHidden : function(hidden){
38961         this.hidden = hidden;
38962         this.linode.setStyle("display", hidden ? "none" : "");
38963     },
38964
38965     /**
38966      * Returns true if this tab is "hidden"
38967      * @return {Boolean}
38968      */
38969     isHidden : function(){
38970         return this.hidden;
38971     },
38972
38973     /**
38974      * Returns the text for this tab
38975      * @return {String}
38976      */
38977     getText : function(){
38978         return this.text;
38979     },
38980     /*
38981     autoSize : function(){
38982         //this.el.beginMeasure();
38983         this.textEl.setWidth(1);
38984         /*
38985          *  #2804 [new] Tabs in Roojs
38986          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38987          */
38988         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38989         //this.el.endMeasure();
38990     //},
38991
38992     /**
38993      * Sets the text for the tab (Note: this also sets the tooltip text)
38994      * @param {String} text The tab's text and tooltip
38995      */
38996     setText : function(text){
38997         this.text = text;
38998         this.textEl.update(text);
38999         this.setTooltip(text);
39000         //if(!this.tabPanel.resizeTabs){
39001         //    this.autoSize();
39002         //}
39003     },
39004     /**
39005      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
39006      */
39007     activate : function(){
39008         this.tabPanel.activate(this.id);
39009     },
39010
39011     /**
39012      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
39013      */
39014     disable : function(){
39015         if(this.tabPanel.active != this){
39016             this.disabled = true;
39017             this.status_node.addClass("disabled");
39018         }
39019     },
39020
39021     /**
39022      * Enables this TabPanelItem if it was previously disabled.
39023      */
39024     enable : function(){
39025         this.disabled = false;
39026         this.status_node.removeClass("disabled");
39027     },
39028
39029     /**
39030      * Sets the content for this TabPanelItem.
39031      * @param {String} content The content
39032      * @param {Boolean} loadScripts true to look for and load scripts
39033      */
39034     setContent : function(content, loadScripts){
39035         this.bodyEl.update(content, loadScripts);
39036     },
39037
39038     /**
39039      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
39040      * @return {Roo.UpdateManager} The UpdateManager
39041      */
39042     getUpdateManager : function(){
39043         return this.bodyEl.getUpdateManager();
39044     },
39045
39046     /**
39047      * Set a URL to be used to load the content for this TabPanelItem.
39048      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
39049      * @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)
39050      * @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)
39051      * @return {Roo.UpdateManager} The UpdateManager
39052      */
39053     setUrl : function(url, params, loadOnce){
39054         if(this.refreshDelegate){
39055             this.un('activate', this.refreshDelegate);
39056         }
39057         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39058         this.on("activate", this.refreshDelegate);
39059         return this.bodyEl.getUpdateManager();
39060     },
39061
39062     /** @private */
39063     _handleRefresh : function(url, params, loadOnce){
39064         if(!loadOnce || !this.loaded){
39065             var updater = this.bodyEl.getUpdateManager();
39066             updater.update(url, params, this._setLoaded.createDelegate(this));
39067         }
39068     },
39069
39070     /**
39071      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
39072      *   Will fail silently if the setUrl method has not been called.
39073      *   This does not activate the panel, just updates its content.
39074      */
39075     refresh : function(){
39076         if(this.refreshDelegate){
39077            this.loaded = false;
39078            this.refreshDelegate();
39079         }
39080     },
39081
39082     /** @private */
39083     _setLoaded : function(){
39084         this.loaded = true;
39085     },
39086
39087     /** @private */
39088     closeClick : function(e){
39089         var o = {};
39090         e.stopEvent();
39091         this.fireEvent("beforeclose", this, o);
39092         if(o.cancel !== true){
39093             this.tabPanel.removeTab(this.id);
39094         }
39095     },
39096     /**
39097      * The text displayed in the tooltip for the close icon.
39098      * @type String
39099      */
39100     closeText : "Close this tab"
39101 });
39102 /**
39103 *    This script refer to:
39104 *    Title: International Telephone Input
39105 *    Author: Jack O'Connor
39106 *    Code version:  v12.1.12
39107 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39108 **/
39109
39110 Roo.bootstrap.PhoneInputData = function() {
39111     var d = [
39112       [
39113         "Afghanistan (‫افغانستان‬‎)",
39114         "af",
39115         "93"
39116       ],
39117       [
39118         "Albania (Shqipëri)",
39119         "al",
39120         "355"
39121       ],
39122       [
39123         "Algeria (‫الجزائر‬‎)",
39124         "dz",
39125         "213"
39126       ],
39127       [
39128         "American Samoa",
39129         "as",
39130         "1684"
39131       ],
39132       [
39133         "Andorra",
39134         "ad",
39135         "376"
39136       ],
39137       [
39138         "Angola",
39139         "ao",
39140         "244"
39141       ],
39142       [
39143         "Anguilla",
39144         "ai",
39145         "1264"
39146       ],
39147       [
39148         "Antigua and Barbuda",
39149         "ag",
39150         "1268"
39151       ],
39152       [
39153         "Argentina",
39154         "ar",
39155         "54"
39156       ],
39157       [
39158         "Armenia (Հայաստան)",
39159         "am",
39160         "374"
39161       ],
39162       [
39163         "Aruba",
39164         "aw",
39165         "297"
39166       ],
39167       [
39168         "Australia",
39169         "au",
39170         "61",
39171         0
39172       ],
39173       [
39174         "Austria (Österreich)",
39175         "at",
39176         "43"
39177       ],
39178       [
39179         "Azerbaijan (Azərbaycan)",
39180         "az",
39181         "994"
39182       ],
39183       [
39184         "Bahamas",
39185         "bs",
39186         "1242"
39187       ],
39188       [
39189         "Bahrain (‫البحرين‬‎)",
39190         "bh",
39191         "973"
39192       ],
39193       [
39194         "Bangladesh (বাংলাদেশ)",
39195         "bd",
39196         "880"
39197       ],
39198       [
39199         "Barbados",
39200         "bb",
39201         "1246"
39202       ],
39203       [
39204         "Belarus (Беларусь)",
39205         "by",
39206         "375"
39207       ],
39208       [
39209         "Belgium (België)",
39210         "be",
39211         "32"
39212       ],
39213       [
39214         "Belize",
39215         "bz",
39216         "501"
39217       ],
39218       [
39219         "Benin (Bénin)",
39220         "bj",
39221         "229"
39222       ],
39223       [
39224         "Bermuda",
39225         "bm",
39226         "1441"
39227       ],
39228       [
39229         "Bhutan (འབྲུག)",
39230         "bt",
39231         "975"
39232       ],
39233       [
39234         "Bolivia",
39235         "bo",
39236         "591"
39237       ],
39238       [
39239         "Bosnia and Herzegovina (Босна и Херцеговина)",
39240         "ba",
39241         "387"
39242       ],
39243       [
39244         "Botswana",
39245         "bw",
39246         "267"
39247       ],
39248       [
39249         "Brazil (Brasil)",
39250         "br",
39251         "55"
39252       ],
39253       [
39254         "British Indian Ocean Territory",
39255         "io",
39256         "246"
39257       ],
39258       [
39259         "British Virgin Islands",
39260         "vg",
39261         "1284"
39262       ],
39263       [
39264         "Brunei",
39265         "bn",
39266         "673"
39267       ],
39268       [
39269         "Bulgaria (България)",
39270         "bg",
39271         "359"
39272       ],
39273       [
39274         "Burkina Faso",
39275         "bf",
39276         "226"
39277       ],
39278       [
39279         "Burundi (Uburundi)",
39280         "bi",
39281         "257"
39282       ],
39283       [
39284         "Cambodia (កម្ពុជា)",
39285         "kh",
39286         "855"
39287       ],
39288       [
39289         "Cameroon (Cameroun)",
39290         "cm",
39291         "237"
39292       ],
39293       [
39294         "Canada",
39295         "ca",
39296         "1",
39297         1,
39298         ["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"]
39299       ],
39300       [
39301         "Cape Verde (Kabu Verdi)",
39302         "cv",
39303         "238"
39304       ],
39305       [
39306         "Caribbean Netherlands",
39307         "bq",
39308         "599",
39309         1
39310       ],
39311       [
39312         "Cayman Islands",
39313         "ky",
39314         "1345"
39315       ],
39316       [
39317         "Central African Republic (République centrafricaine)",
39318         "cf",
39319         "236"
39320       ],
39321       [
39322         "Chad (Tchad)",
39323         "td",
39324         "235"
39325       ],
39326       [
39327         "Chile",
39328         "cl",
39329         "56"
39330       ],
39331       [
39332         "China (中国)",
39333         "cn",
39334         "86"
39335       ],
39336       [
39337         "Christmas Island",
39338         "cx",
39339         "61",
39340         2
39341       ],
39342       [
39343         "Cocos (Keeling) Islands",
39344         "cc",
39345         "61",
39346         1
39347       ],
39348       [
39349         "Colombia",
39350         "co",
39351         "57"
39352       ],
39353       [
39354         "Comoros (‫جزر القمر‬‎)",
39355         "km",
39356         "269"
39357       ],
39358       [
39359         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39360         "cd",
39361         "243"
39362       ],
39363       [
39364         "Congo (Republic) (Congo-Brazzaville)",
39365         "cg",
39366         "242"
39367       ],
39368       [
39369         "Cook Islands",
39370         "ck",
39371         "682"
39372       ],
39373       [
39374         "Costa Rica",
39375         "cr",
39376         "506"
39377       ],
39378       [
39379         "Côte d’Ivoire",
39380         "ci",
39381         "225"
39382       ],
39383       [
39384         "Croatia (Hrvatska)",
39385         "hr",
39386         "385"
39387       ],
39388       [
39389         "Cuba",
39390         "cu",
39391         "53"
39392       ],
39393       [
39394         "Curaçao",
39395         "cw",
39396         "599",
39397         0
39398       ],
39399       [
39400         "Cyprus (Κύπρος)",
39401         "cy",
39402         "357"
39403       ],
39404       [
39405         "Czech Republic (Česká republika)",
39406         "cz",
39407         "420"
39408       ],
39409       [
39410         "Denmark (Danmark)",
39411         "dk",
39412         "45"
39413       ],
39414       [
39415         "Djibouti",
39416         "dj",
39417         "253"
39418       ],
39419       [
39420         "Dominica",
39421         "dm",
39422         "1767"
39423       ],
39424       [
39425         "Dominican Republic (República Dominicana)",
39426         "do",
39427         "1",
39428         2,
39429         ["809", "829", "849"]
39430       ],
39431       [
39432         "Ecuador",
39433         "ec",
39434         "593"
39435       ],
39436       [
39437         "Egypt (‫مصر‬‎)",
39438         "eg",
39439         "20"
39440       ],
39441       [
39442         "El Salvador",
39443         "sv",
39444         "503"
39445       ],
39446       [
39447         "Equatorial Guinea (Guinea Ecuatorial)",
39448         "gq",
39449         "240"
39450       ],
39451       [
39452         "Eritrea",
39453         "er",
39454         "291"
39455       ],
39456       [
39457         "Estonia (Eesti)",
39458         "ee",
39459         "372"
39460       ],
39461       [
39462         "Ethiopia",
39463         "et",
39464         "251"
39465       ],
39466       [
39467         "Falkland Islands (Islas Malvinas)",
39468         "fk",
39469         "500"
39470       ],
39471       [
39472         "Faroe Islands (Føroyar)",
39473         "fo",
39474         "298"
39475       ],
39476       [
39477         "Fiji",
39478         "fj",
39479         "679"
39480       ],
39481       [
39482         "Finland (Suomi)",
39483         "fi",
39484         "358",
39485         0
39486       ],
39487       [
39488         "France",
39489         "fr",
39490         "33"
39491       ],
39492       [
39493         "French Guiana (Guyane française)",
39494         "gf",
39495         "594"
39496       ],
39497       [
39498         "French Polynesia (Polynésie française)",
39499         "pf",
39500         "689"
39501       ],
39502       [
39503         "Gabon",
39504         "ga",
39505         "241"
39506       ],
39507       [
39508         "Gambia",
39509         "gm",
39510         "220"
39511       ],
39512       [
39513         "Georgia (საქართველო)",
39514         "ge",
39515         "995"
39516       ],
39517       [
39518         "Germany (Deutschland)",
39519         "de",
39520         "49"
39521       ],
39522       [
39523         "Ghana (Gaana)",
39524         "gh",
39525         "233"
39526       ],
39527       [
39528         "Gibraltar",
39529         "gi",
39530         "350"
39531       ],
39532       [
39533         "Greece (Ελλάδα)",
39534         "gr",
39535         "30"
39536       ],
39537       [
39538         "Greenland (Kalaallit Nunaat)",
39539         "gl",
39540         "299"
39541       ],
39542       [
39543         "Grenada",
39544         "gd",
39545         "1473"
39546       ],
39547       [
39548         "Guadeloupe",
39549         "gp",
39550         "590",
39551         0
39552       ],
39553       [
39554         "Guam",
39555         "gu",
39556         "1671"
39557       ],
39558       [
39559         "Guatemala",
39560         "gt",
39561         "502"
39562       ],
39563       [
39564         "Guernsey",
39565         "gg",
39566         "44",
39567         1
39568       ],
39569       [
39570         "Guinea (Guinée)",
39571         "gn",
39572         "224"
39573       ],
39574       [
39575         "Guinea-Bissau (Guiné Bissau)",
39576         "gw",
39577         "245"
39578       ],
39579       [
39580         "Guyana",
39581         "gy",
39582         "592"
39583       ],
39584       [
39585         "Haiti",
39586         "ht",
39587         "509"
39588       ],
39589       [
39590         "Honduras",
39591         "hn",
39592         "504"
39593       ],
39594       [
39595         "Hong Kong (香港)",
39596         "hk",
39597         "852"
39598       ],
39599       [
39600         "Hungary (Magyarország)",
39601         "hu",
39602         "36"
39603       ],
39604       [
39605         "Iceland (Ísland)",
39606         "is",
39607         "354"
39608       ],
39609       [
39610         "India (भारत)",
39611         "in",
39612         "91"
39613       ],
39614       [
39615         "Indonesia",
39616         "id",
39617         "62"
39618       ],
39619       [
39620         "Iran (‫ایران‬‎)",
39621         "ir",
39622         "98"
39623       ],
39624       [
39625         "Iraq (‫العراق‬‎)",
39626         "iq",
39627         "964"
39628       ],
39629       [
39630         "Ireland",
39631         "ie",
39632         "353"
39633       ],
39634       [
39635         "Isle of Man",
39636         "im",
39637         "44",
39638         2
39639       ],
39640       [
39641         "Israel (‫ישראל‬‎)",
39642         "il",
39643         "972"
39644       ],
39645       [
39646         "Italy (Italia)",
39647         "it",
39648         "39",
39649         0
39650       ],
39651       [
39652         "Jamaica",
39653         "jm",
39654         "1876"
39655       ],
39656       [
39657         "Japan (日本)",
39658         "jp",
39659         "81"
39660       ],
39661       [
39662         "Jersey",
39663         "je",
39664         "44",
39665         3
39666       ],
39667       [
39668         "Jordan (‫الأردن‬‎)",
39669         "jo",
39670         "962"
39671       ],
39672       [
39673         "Kazakhstan (Казахстан)",
39674         "kz",
39675         "7",
39676         1
39677       ],
39678       [
39679         "Kenya",
39680         "ke",
39681         "254"
39682       ],
39683       [
39684         "Kiribati",
39685         "ki",
39686         "686"
39687       ],
39688       [
39689         "Kosovo",
39690         "xk",
39691         "383"
39692       ],
39693       [
39694         "Kuwait (‫الكويت‬‎)",
39695         "kw",
39696         "965"
39697       ],
39698       [
39699         "Kyrgyzstan (Кыргызстан)",
39700         "kg",
39701         "996"
39702       ],
39703       [
39704         "Laos (ລາວ)",
39705         "la",
39706         "856"
39707       ],
39708       [
39709         "Latvia (Latvija)",
39710         "lv",
39711         "371"
39712       ],
39713       [
39714         "Lebanon (‫لبنان‬‎)",
39715         "lb",
39716         "961"
39717       ],
39718       [
39719         "Lesotho",
39720         "ls",
39721         "266"
39722       ],
39723       [
39724         "Liberia",
39725         "lr",
39726         "231"
39727       ],
39728       [
39729         "Libya (‫ليبيا‬‎)",
39730         "ly",
39731         "218"
39732       ],
39733       [
39734         "Liechtenstein",
39735         "li",
39736         "423"
39737       ],
39738       [
39739         "Lithuania (Lietuva)",
39740         "lt",
39741         "370"
39742       ],
39743       [
39744         "Luxembourg",
39745         "lu",
39746         "352"
39747       ],
39748       [
39749         "Macau (澳門)",
39750         "mo",
39751         "853"
39752       ],
39753       [
39754         "Macedonia (FYROM) (Македонија)",
39755         "mk",
39756         "389"
39757       ],
39758       [
39759         "Madagascar (Madagasikara)",
39760         "mg",
39761         "261"
39762       ],
39763       [
39764         "Malawi",
39765         "mw",
39766         "265"
39767       ],
39768       [
39769         "Malaysia",
39770         "my",
39771         "60"
39772       ],
39773       [
39774         "Maldives",
39775         "mv",
39776         "960"
39777       ],
39778       [
39779         "Mali",
39780         "ml",
39781         "223"
39782       ],
39783       [
39784         "Malta",
39785         "mt",
39786         "356"
39787       ],
39788       [
39789         "Marshall Islands",
39790         "mh",
39791         "692"
39792       ],
39793       [
39794         "Martinique",
39795         "mq",
39796         "596"
39797       ],
39798       [
39799         "Mauritania (‫موريتانيا‬‎)",
39800         "mr",
39801         "222"
39802       ],
39803       [
39804         "Mauritius (Moris)",
39805         "mu",
39806         "230"
39807       ],
39808       [
39809         "Mayotte",
39810         "yt",
39811         "262",
39812         1
39813       ],
39814       [
39815         "Mexico (México)",
39816         "mx",
39817         "52"
39818       ],
39819       [
39820         "Micronesia",
39821         "fm",
39822         "691"
39823       ],
39824       [
39825         "Moldova (Republica Moldova)",
39826         "md",
39827         "373"
39828       ],
39829       [
39830         "Monaco",
39831         "mc",
39832         "377"
39833       ],
39834       [
39835         "Mongolia (Монгол)",
39836         "mn",
39837         "976"
39838       ],
39839       [
39840         "Montenegro (Crna Gora)",
39841         "me",
39842         "382"
39843       ],
39844       [
39845         "Montserrat",
39846         "ms",
39847         "1664"
39848       ],
39849       [
39850         "Morocco (‫المغرب‬‎)",
39851         "ma",
39852         "212",
39853         0
39854       ],
39855       [
39856         "Mozambique (Moçambique)",
39857         "mz",
39858         "258"
39859       ],
39860       [
39861         "Myanmar (Burma) (မြန်မာ)",
39862         "mm",
39863         "95"
39864       ],
39865       [
39866         "Namibia (Namibië)",
39867         "na",
39868         "264"
39869       ],
39870       [
39871         "Nauru",
39872         "nr",
39873         "674"
39874       ],
39875       [
39876         "Nepal (नेपाल)",
39877         "np",
39878         "977"
39879       ],
39880       [
39881         "Netherlands (Nederland)",
39882         "nl",
39883         "31"
39884       ],
39885       [
39886         "New Caledonia (Nouvelle-Calédonie)",
39887         "nc",
39888         "687"
39889       ],
39890       [
39891         "New Zealand",
39892         "nz",
39893         "64"
39894       ],
39895       [
39896         "Nicaragua",
39897         "ni",
39898         "505"
39899       ],
39900       [
39901         "Niger (Nijar)",
39902         "ne",
39903         "227"
39904       ],
39905       [
39906         "Nigeria",
39907         "ng",
39908         "234"
39909       ],
39910       [
39911         "Niue",
39912         "nu",
39913         "683"
39914       ],
39915       [
39916         "Norfolk Island",
39917         "nf",
39918         "672"
39919       ],
39920       [
39921         "North Korea (조선 민주주의 인민 공화국)",
39922         "kp",
39923         "850"
39924       ],
39925       [
39926         "Northern Mariana Islands",
39927         "mp",
39928         "1670"
39929       ],
39930       [
39931         "Norway (Norge)",
39932         "no",
39933         "47",
39934         0
39935       ],
39936       [
39937         "Oman (‫عُمان‬‎)",
39938         "om",
39939         "968"
39940       ],
39941       [
39942         "Pakistan (‫پاکستان‬‎)",
39943         "pk",
39944         "92"
39945       ],
39946       [
39947         "Palau",
39948         "pw",
39949         "680"
39950       ],
39951       [
39952         "Palestine (‫فلسطين‬‎)",
39953         "ps",
39954         "970"
39955       ],
39956       [
39957         "Panama (Panamá)",
39958         "pa",
39959         "507"
39960       ],
39961       [
39962         "Papua New Guinea",
39963         "pg",
39964         "675"
39965       ],
39966       [
39967         "Paraguay",
39968         "py",
39969         "595"
39970       ],
39971       [
39972         "Peru (Perú)",
39973         "pe",
39974         "51"
39975       ],
39976       [
39977         "Philippines",
39978         "ph",
39979         "63"
39980       ],
39981       [
39982         "Poland (Polska)",
39983         "pl",
39984         "48"
39985       ],
39986       [
39987         "Portugal",
39988         "pt",
39989         "351"
39990       ],
39991       [
39992         "Puerto Rico",
39993         "pr",
39994         "1",
39995         3,
39996         ["787", "939"]
39997       ],
39998       [
39999         "Qatar (‫قطر‬‎)",
40000         "qa",
40001         "974"
40002       ],
40003       [
40004         "Réunion (La Réunion)",
40005         "re",
40006         "262",
40007         0
40008       ],
40009       [
40010         "Romania (România)",
40011         "ro",
40012         "40"
40013       ],
40014       [
40015         "Russia (Россия)",
40016         "ru",
40017         "7",
40018         0
40019       ],
40020       [
40021         "Rwanda",
40022         "rw",
40023         "250"
40024       ],
40025       [
40026         "Saint Barthélemy",
40027         "bl",
40028         "590",
40029         1
40030       ],
40031       [
40032         "Saint Helena",
40033         "sh",
40034         "290"
40035       ],
40036       [
40037         "Saint Kitts and Nevis",
40038         "kn",
40039         "1869"
40040       ],
40041       [
40042         "Saint Lucia",
40043         "lc",
40044         "1758"
40045       ],
40046       [
40047         "Saint Martin (Saint-Martin (partie française))",
40048         "mf",
40049         "590",
40050         2
40051       ],
40052       [
40053         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
40054         "pm",
40055         "508"
40056       ],
40057       [
40058         "Saint Vincent and the Grenadines",
40059         "vc",
40060         "1784"
40061       ],
40062       [
40063         "Samoa",
40064         "ws",
40065         "685"
40066       ],
40067       [
40068         "San Marino",
40069         "sm",
40070         "378"
40071       ],
40072       [
40073         "São Tomé and Príncipe (São Tomé e Príncipe)",
40074         "st",
40075         "239"
40076       ],
40077       [
40078         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
40079         "sa",
40080         "966"
40081       ],
40082       [
40083         "Senegal (Sénégal)",
40084         "sn",
40085         "221"
40086       ],
40087       [
40088         "Serbia (Србија)",
40089         "rs",
40090         "381"
40091       ],
40092       [
40093         "Seychelles",
40094         "sc",
40095         "248"
40096       ],
40097       [
40098         "Sierra Leone",
40099         "sl",
40100         "232"
40101       ],
40102       [
40103         "Singapore",
40104         "sg",
40105         "65"
40106       ],
40107       [
40108         "Sint Maarten",
40109         "sx",
40110         "1721"
40111       ],
40112       [
40113         "Slovakia (Slovensko)",
40114         "sk",
40115         "421"
40116       ],
40117       [
40118         "Slovenia (Slovenija)",
40119         "si",
40120         "386"
40121       ],
40122       [
40123         "Solomon Islands",
40124         "sb",
40125         "677"
40126       ],
40127       [
40128         "Somalia (Soomaaliya)",
40129         "so",
40130         "252"
40131       ],
40132       [
40133         "South Africa",
40134         "za",
40135         "27"
40136       ],
40137       [
40138         "South Korea (대한민국)",
40139         "kr",
40140         "82"
40141       ],
40142       [
40143         "South Sudan (‫جنوب السودان‬‎)",
40144         "ss",
40145         "211"
40146       ],
40147       [
40148         "Spain (España)",
40149         "es",
40150         "34"
40151       ],
40152       [
40153         "Sri Lanka (ශ්‍රී ලංකාව)",
40154         "lk",
40155         "94"
40156       ],
40157       [
40158         "Sudan (‫السودان‬‎)",
40159         "sd",
40160         "249"
40161       ],
40162       [
40163         "Suriname",
40164         "sr",
40165         "597"
40166       ],
40167       [
40168         "Svalbard and Jan Mayen",
40169         "sj",
40170         "47",
40171         1
40172       ],
40173       [
40174         "Swaziland",
40175         "sz",
40176         "268"
40177       ],
40178       [
40179         "Sweden (Sverige)",
40180         "se",
40181         "46"
40182       ],
40183       [
40184         "Switzerland (Schweiz)",
40185         "ch",
40186         "41"
40187       ],
40188       [
40189         "Syria (‫سوريا‬‎)",
40190         "sy",
40191         "963"
40192       ],
40193       [
40194         "Taiwan (台灣)",
40195         "tw",
40196         "886"
40197       ],
40198       [
40199         "Tajikistan",
40200         "tj",
40201         "992"
40202       ],
40203       [
40204         "Tanzania",
40205         "tz",
40206         "255"
40207       ],
40208       [
40209         "Thailand (ไทย)",
40210         "th",
40211         "66"
40212       ],
40213       [
40214         "Timor-Leste",
40215         "tl",
40216         "670"
40217       ],
40218       [
40219         "Togo",
40220         "tg",
40221         "228"
40222       ],
40223       [
40224         "Tokelau",
40225         "tk",
40226         "690"
40227       ],
40228       [
40229         "Tonga",
40230         "to",
40231         "676"
40232       ],
40233       [
40234         "Trinidad and Tobago",
40235         "tt",
40236         "1868"
40237       ],
40238       [
40239         "Tunisia (‫تونس‬‎)",
40240         "tn",
40241         "216"
40242       ],
40243       [
40244         "Turkey (Türkiye)",
40245         "tr",
40246         "90"
40247       ],
40248       [
40249         "Turkmenistan",
40250         "tm",
40251         "993"
40252       ],
40253       [
40254         "Turks and Caicos Islands",
40255         "tc",
40256         "1649"
40257       ],
40258       [
40259         "Tuvalu",
40260         "tv",
40261         "688"
40262       ],
40263       [
40264         "U.S. Virgin Islands",
40265         "vi",
40266         "1340"
40267       ],
40268       [
40269         "Uganda",
40270         "ug",
40271         "256"
40272       ],
40273       [
40274         "Ukraine (Україна)",
40275         "ua",
40276         "380"
40277       ],
40278       [
40279         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40280         "ae",
40281         "971"
40282       ],
40283       [
40284         "United Kingdom",
40285         "gb",
40286         "44",
40287         0
40288       ],
40289       [
40290         "United States",
40291         "us",
40292         "1",
40293         0
40294       ],
40295       [
40296         "Uruguay",
40297         "uy",
40298         "598"
40299       ],
40300       [
40301         "Uzbekistan (Oʻzbekiston)",
40302         "uz",
40303         "998"
40304       ],
40305       [
40306         "Vanuatu",
40307         "vu",
40308         "678"
40309       ],
40310       [
40311         "Vatican City (Città del Vaticano)",
40312         "va",
40313         "39",
40314         1
40315       ],
40316       [
40317         "Venezuela",
40318         "ve",
40319         "58"
40320       ],
40321       [
40322         "Vietnam (Việt Nam)",
40323         "vn",
40324         "84"
40325       ],
40326       [
40327         "Wallis and Futuna (Wallis-et-Futuna)",
40328         "wf",
40329         "681"
40330       ],
40331       [
40332         "Western Sahara (‫الصحراء الغربية‬‎)",
40333         "eh",
40334         "212",
40335         1
40336       ],
40337       [
40338         "Yemen (‫اليمن‬‎)",
40339         "ye",
40340         "967"
40341       ],
40342       [
40343         "Zambia",
40344         "zm",
40345         "260"
40346       ],
40347       [
40348         "Zimbabwe",
40349         "zw",
40350         "263"
40351       ],
40352       [
40353         "Åland Islands",
40354         "ax",
40355         "358",
40356         1
40357       ]
40358   ];
40359   
40360   return d;
40361 }/**
40362 *    This script refer to:
40363 *    Title: International Telephone Input
40364 *    Author: Jack O'Connor
40365 *    Code version:  v12.1.12
40366 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40367 **/
40368
40369 /**
40370  * @class Roo.bootstrap.PhoneInput
40371  * @extends Roo.bootstrap.TriggerField
40372  * An input with International dial-code selection
40373  
40374  * @cfg {String} defaultDialCode default '+852'
40375  * @cfg {Array} preferedCountries default []
40376   
40377  * @constructor
40378  * Create a new PhoneInput.
40379  * @param {Object} config Configuration options
40380  */
40381
40382 Roo.bootstrap.PhoneInput = function(config) {
40383     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40384 };
40385
40386 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40387         
40388         listWidth: undefined,
40389         
40390         selectedClass: 'active',
40391         
40392         invalidClass : "has-warning",
40393         
40394         validClass: 'has-success',
40395         
40396         allowed: '0123456789',
40397         
40398         max_length: 15,
40399         
40400         /**
40401          * @cfg {String} defaultDialCode The default dial code when initializing the input
40402          */
40403         defaultDialCode: '+852',
40404         
40405         /**
40406          * @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
40407          */
40408         preferedCountries: false,
40409         
40410         getAutoCreate : function()
40411         {
40412             var data = Roo.bootstrap.PhoneInputData();
40413             var align = this.labelAlign || this.parentLabelAlign();
40414             var id = Roo.id();
40415             
40416             this.allCountries = [];
40417             this.dialCodeMapping = [];
40418             
40419             for (var i = 0; i < data.length; i++) {
40420               var c = data[i];
40421               this.allCountries[i] = {
40422                 name: c[0],
40423                 iso2: c[1],
40424                 dialCode: c[2],
40425                 priority: c[3] || 0,
40426                 areaCodes: c[4] || null
40427               };
40428               this.dialCodeMapping[c[2]] = {
40429                   name: c[0],
40430                   iso2: c[1],
40431                   priority: c[3] || 0,
40432                   areaCodes: c[4] || null
40433               };
40434             }
40435             
40436             var cfg = {
40437                 cls: 'form-group',
40438                 cn: []
40439             };
40440             
40441             var input =  {
40442                 tag: 'input',
40443                 id : id,
40444                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40445                 maxlength: this.max_length,
40446                 cls : 'form-control tel-input',
40447                 autocomplete: 'new-password'
40448             };
40449             
40450             var hiddenInput = {
40451                 tag: 'input',
40452                 type: 'hidden',
40453                 cls: 'hidden-tel-input'
40454             };
40455             
40456             if (this.name) {
40457                 hiddenInput.name = this.name;
40458             }
40459             
40460             if (this.disabled) {
40461                 input.disabled = true;
40462             }
40463             
40464             var flag_container = {
40465                 tag: 'div',
40466                 cls: 'flag-box',
40467                 cn: [
40468                     {
40469                         tag: 'div',
40470                         cls: 'flag'
40471                     },
40472                     {
40473                         tag: 'div',
40474                         cls: 'caret'
40475                     }
40476                 ]
40477             };
40478             
40479             var box = {
40480                 tag: 'div',
40481                 cls: this.hasFeedback ? 'has-feedback' : '',
40482                 cn: [
40483                     hiddenInput,
40484                     input,
40485                     {
40486                         tag: 'input',
40487                         cls: 'dial-code-holder',
40488                         disabled: true
40489                     }
40490                 ]
40491             };
40492             
40493             var container = {
40494                 cls: 'roo-select2-container input-group',
40495                 cn: [
40496                     flag_container,
40497                     box
40498                 ]
40499             };
40500             
40501             if (this.fieldLabel.length) {
40502                 var indicator = {
40503                     tag: 'i',
40504                     tooltip: 'This field is required'
40505                 };
40506                 
40507                 var label = {
40508                     tag: 'label',
40509                     'for':  id,
40510                     cls: 'control-label',
40511                     cn: []
40512                 };
40513                 
40514                 var label_text = {
40515                     tag: 'span',
40516                     html: this.fieldLabel
40517                 };
40518                 
40519                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40520                 label.cn = [
40521                     indicator,
40522                     label_text
40523                 ];
40524                 
40525                 if(this.indicatorpos == 'right') {
40526                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40527                     label.cn = [
40528                         label_text,
40529                         indicator
40530                     ];
40531                 }
40532                 
40533                 if(align == 'left') {
40534                     container = {
40535                         tag: 'div',
40536                         cn: [
40537                             container
40538                         ]
40539                     };
40540                     
40541                     if(this.labelWidth > 12){
40542                         label.style = "width: " + this.labelWidth + 'px';
40543                     }
40544                     if(this.labelWidth < 13 && this.labelmd == 0){
40545                         this.labelmd = this.labelWidth;
40546                     }
40547                     if(this.labellg > 0){
40548                         label.cls += ' col-lg-' + this.labellg;
40549                         input.cls += ' col-lg-' + (12 - this.labellg);
40550                     }
40551                     if(this.labelmd > 0){
40552                         label.cls += ' col-md-' + this.labelmd;
40553                         container.cls += ' col-md-' + (12 - this.labelmd);
40554                     }
40555                     if(this.labelsm > 0){
40556                         label.cls += ' col-sm-' + this.labelsm;
40557                         container.cls += ' col-sm-' + (12 - this.labelsm);
40558                     }
40559                     if(this.labelxs > 0){
40560                         label.cls += ' col-xs-' + this.labelxs;
40561                         container.cls += ' col-xs-' + (12 - this.labelxs);
40562                     }
40563                 }
40564             }
40565             
40566             cfg.cn = [
40567                 label,
40568                 container
40569             ];
40570             
40571             var settings = this;
40572             
40573             ['xs','sm','md','lg'].map(function(size){
40574                 if (settings[size]) {
40575                     cfg.cls += ' col-' + size + '-' + settings[size];
40576                 }
40577             });
40578             
40579             this.store = new Roo.data.Store({
40580                 proxy : new Roo.data.MemoryProxy({}),
40581                 reader : new Roo.data.JsonReader({
40582                     fields : [
40583                         {
40584                             'name' : 'name',
40585                             'type' : 'string'
40586                         },
40587                         {
40588                             'name' : 'iso2',
40589                             'type' : 'string'
40590                         },
40591                         {
40592                             'name' : 'dialCode',
40593                             'type' : 'string'
40594                         },
40595                         {
40596                             'name' : 'priority',
40597                             'type' : 'string'
40598                         },
40599                         {
40600                             'name' : 'areaCodes',
40601                             'type' : 'string'
40602                         }
40603                     ]
40604                 })
40605             });
40606             
40607             if(!this.preferedCountries) {
40608                 this.preferedCountries = [
40609                     'hk',
40610                     'gb',
40611                     'us'
40612                 ];
40613             }
40614             
40615             var p = this.preferedCountries.reverse();
40616             
40617             if(p) {
40618                 for (var i = 0; i < p.length; i++) {
40619                     for (var j = 0; j < this.allCountries.length; j++) {
40620                         if(this.allCountries[j].iso2 == p[i]) {
40621                             var t = this.allCountries[j];
40622                             this.allCountries.splice(j,1);
40623                             this.allCountries.unshift(t);
40624                         }
40625                     } 
40626                 }
40627             }
40628             
40629             this.store.proxy.data = {
40630                 success: true,
40631                 data: this.allCountries
40632             };
40633             
40634             return cfg;
40635         },
40636         
40637         initEvents : function()
40638         {
40639             this.createList();
40640             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40641             
40642             this.indicator = this.indicatorEl();
40643             this.flag = this.flagEl();
40644             this.dialCodeHolder = this.dialCodeHolderEl();
40645             
40646             this.trigger = this.el.select('div.flag-box',true).first();
40647             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40648             
40649             var _this = this;
40650             
40651             (function(){
40652                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40653                 _this.list.setWidth(lw);
40654             }).defer(100);
40655             
40656             this.list.on('mouseover', this.onViewOver, this);
40657             this.list.on('mousemove', this.onViewMove, this);
40658             this.inputEl().on("keyup", this.onKeyUp, this);
40659             this.inputEl().on("keypress", this.onKeyPress, this);
40660             
40661             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40662
40663             this.view = new Roo.View(this.list, this.tpl, {
40664                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40665             });
40666             
40667             this.view.on('click', this.onViewClick, this);
40668             this.setValue(this.defaultDialCode);
40669         },
40670         
40671         onTriggerClick : function(e)
40672         {
40673             Roo.log('trigger click');
40674             if(this.disabled){
40675                 return;
40676             }
40677             
40678             if(this.isExpanded()){
40679                 this.collapse();
40680                 this.hasFocus = false;
40681             }else {
40682                 this.store.load({});
40683                 this.hasFocus = true;
40684                 this.expand();
40685             }
40686         },
40687         
40688         isExpanded : function()
40689         {
40690             return this.list.isVisible();
40691         },
40692         
40693         collapse : function()
40694         {
40695             if(!this.isExpanded()){
40696                 return;
40697             }
40698             this.list.hide();
40699             Roo.get(document).un('mousedown', this.collapseIf, this);
40700             Roo.get(document).un('mousewheel', this.collapseIf, this);
40701             this.fireEvent('collapse', this);
40702             this.validate();
40703         },
40704         
40705         expand : function()
40706         {
40707             Roo.log('expand');
40708
40709             if(this.isExpanded() || !this.hasFocus){
40710                 return;
40711             }
40712             
40713             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40714             this.list.setWidth(lw);
40715             
40716             this.list.show();
40717             this.restrictHeight();
40718             
40719             Roo.get(document).on('mousedown', this.collapseIf, this);
40720             Roo.get(document).on('mousewheel', this.collapseIf, this);
40721             
40722             this.fireEvent('expand', this);
40723         },
40724         
40725         restrictHeight : function()
40726         {
40727             this.list.alignTo(this.inputEl(), this.listAlign);
40728             this.list.alignTo(this.inputEl(), this.listAlign);
40729         },
40730         
40731         onViewOver : function(e, t)
40732         {
40733             if(this.inKeyMode){
40734                 return;
40735             }
40736             var item = this.view.findItemFromChild(t);
40737             
40738             if(item){
40739                 var index = this.view.indexOf(item);
40740                 this.select(index, false);
40741             }
40742         },
40743
40744         // private
40745         onViewClick : function(view, doFocus, el, e)
40746         {
40747             var index = this.view.getSelectedIndexes()[0];
40748             
40749             var r = this.store.getAt(index);
40750             
40751             if(r){
40752                 this.onSelect(r, index);
40753             }
40754             if(doFocus !== false && !this.blockFocus){
40755                 this.inputEl().focus();
40756             }
40757         },
40758         
40759         onViewMove : function(e, t)
40760         {
40761             this.inKeyMode = false;
40762         },
40763         
40764         select : function(index, scrollIntoView)
40765         {
40766             this.selectedIndex = index;
40767             this.view.select(index);
40768             if(scrollIntoView !== false){
40769                 var el = this.view.getNode(index);
40770                 if(el){
40771                     this.list.scrollChildIntoView(el, false);
40772                 }
40773             }
40774         },
40775         
40776         createList : function()
40777         {
40778             this.list = Roo.get(document.body).createChild({
40779                 tag: 'ul',
40780                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40781                 style: 'display:none'
40782             });
40783             
40784             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40785         },
40786         
40787         collapseIf : function(e)
40788         {
40789             var in_combo  = e.within(this.el);
40790             var in_list =  e.within(this.list);
40791             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40792             
40793             if (in_combo || in_list || is_list) {
40794                 return;
40795             }
40796             this.collapse();
40797         },
40798         
40799         onSelect : function(record, index)
40800         {
40801             if(this.fireEvent('beforeselect', this, record, index) !== false){
40802                 
40803                 this.setFlagClass(record.data.iso2);
40804                 this.setDialCode(record.data.dialCode);
40805                 this.hasFocus = false;
40806                 this.collapse();
40807                 this.fireEvent('select', this, record, index);
40808             }
40809         },
40810         
40811         flagEl : function()
40812         {
40813             var flag = this.el.select('div.flag',true).first();
40814             if(!flag){
40815                 return false;
40816             }
40817             return flag;
40818         },
40819         
40820         dialCodeHolderEl : function()
40821         {
40822             var d = this.el.select('input.dial-code-holder',true).first();
40823             if(!d){
40824                 return false;
40825             }
40826             return d;
40827         },
40828         
40829         setDialCode : function(v)
40830         {
40831             this.dialCodeHolder.dom.value = '+'+v;
40832         },
40833         
40834         setFlagClass : function(n)
40835         {
40836             this.flag.dom.className = 'flag '+n;
40837         },
40838         
40839         getValue : function()
40840         {
40841             var v = this.inputEl().getValue();
40842             if(this.dialCodeHolder) {
40843                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40844             }
40845             return v;
40846         },
40847         
40848         setValue : function(v)
40849         {
40850             var d = this.getDialCode(v);
40851             
40852             //invalid dial code
40853             if(v.length == 0 || !d || d.length == 0) {
40854                 if(this.rendered){
40855                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40856                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40857                 }
40858                 return;
40859             }
40860             
40861             //valid dial code
40862             this.setFlagClass(this.dialCodeMapping[d].iso2);
40863             this.setDialCode(d);
40864             this.inputEl().dom.value = v.replace('+'+d,'');
40865             this.hiddenEl().dom.value = this.getValue();
40866             
40867             this.validate();
40868         },
40869         
40870         getDialCode : function(v)
40871         {
40872             v = v ||  '';
40873             
40874             if (v.length == 0) {
40875                 return this.dialCodeHolder.dom.value;
40876             }
40877             
40878             var dialCode = "";
40879             if (v.charAt(0) != "+") {
40880                 return false;
40881             }
40882             var numericChars = "";
40883             for (var i = 1; i < v.length; i++) {
40884               var c = v.charAt(i);
40885               if (!isNaN(c)) {
40886                 numericChars += c;
40887                 if (this.dialCodeMapping[numericChars]) {
40888                   dialCode = v.substr(1, i);
40889                 }
40890                 if (numericChars.length == 4) {
40891                   break;
40892                 }
40893               }
40894             }
40895             return dialCode;
40896         },
40897         
40898         reset : function()
40899         {
40900             this.setValue(this.defaultDialCode);
40901             this.validate();
40902         },
40903         
40904         hiddenEl : function()
40905         {
40906             return this.el.select('input.hidden-tel-input',true).first();
40907         },
40908         
40909         // after setting val
40910         onKeyUp : function(e){
40911             this.setValue(this.getValue());
40912         },
40913         
40914         onKeyPress : function(e){
40915             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40916                 e.stopEvent();
40917             }
40918         }
40919         
40920 });
40921 /**
40922  * @class Roo.bootstrap.MoneyField
40923  * @extends Roo.bootstrap.ComboBox
40924  * Bootstrap MoneyField class
40925  * 
40926  * @constructor
40927  * Create a new MoneyField.
40928  * @param {Object} config Configuration options
40929  */
40930
40931 Roo.bootstrap.MoneyField = function(config) {
40932     
40933     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40934     
40935 };
40936
40937 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40938     
40939     /**
40940      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40941      */
40942     allowDecimals : true,
40943     /**
40944      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40945      */
40946     decimalSeparator : ".",
40947     /**
40948      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40949      */
40950     decimalPrecision : 0,
40951     /**
40952      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40953      */
40954     allowNegative : true,
40955     /**
40956      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40957      */
40958     allowZero: true,
40959     /**
40960      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40961      */
40962     minValue : Number.NEGATIVE_INFINITY,
40963     /**
40964      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40965      */
40966     maxValue : Number.MAX_VALUE,
40967     /**
40968      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40969      */
40970     minText : "The minimum value for this field is {0}",
40971     /**
40972      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40973      */
40974     maxText : "The maximum value for this field is {0}",
40975     /**
40976      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40977      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40978      */
40979     nanText : "{0} is not a valid number",
40980     /**
40981      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40982      */
40983     castInt : true,
40984     /**
40985      * @cfg {String} defaults currency of the MoneyField
40986      * value should be in lkey
40987      */
40988     defaultCurrency : false,
40989     /**
40990      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40991      */
40992     thousandsDelimiter : false,
40993     /**
40994      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40995      */
40996     max_length: false,
40997     
40998     inputlg : 9,
40999     inputmd : 9,
41000     inputsm : 9,
41001     inputxs : 6,
41002     
41003     store : false,
41004     
41005     getAutoCreate : function()
41006     {
41007         var align = this.labelAlign || this.parentLabelAlign();
41008         
41009         var id = Roo.id();
41010
41011         var cfg = {
41012             cls: 'form-group',
41013             cn: []
41014         };
41015
41016         var input =  {
41017             tag: 'input',
41018             id : id,
41019             cls : 'form-control roo-money-amount-input',
41020             autocomplete: 'new-password'
41021         };
41022         
41023         var hiddenInput = {
41024             tag: 'input',
41025             type: 'hidden',
41026             id: Roo.id(),
41027             cls: 'hidden-number-input'
41028         };
41029         
41030         if(this.max_length) {
41031             input.maxlength = this.max_length; 
41032         }
41033         
41034         if (this.name) {
41035             hiddenInput.name = this.name;
41036         }
41037
41038         if (this.disabled) {
41039             input.disabled = true;
41040         }
41041
41042         var clg = 12 - this.inputlg;
41043         var cmd = 12 - this.inputmd;
41044         var csm = 12 - this.inputsm;
41045         var cxs = 12 - this.inputxs;
41046         
41047         var container = {
41048             tag : 'div',
41049             cls : 'row roo-money-field',
41050             cn : [
41051                 {
41052                     tag : 'div',
41053                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
41054                     cn : [
41055                         {
41056                             tag : 'div',
41057                             cls: 'roo-select2-container input-group',
41058                             cn: [
41059                                 {
41060                                     tag : 'input',
41061                                     cls : 'form-control roo-money-currency-input',
41062                                     autocomplete: 'new-password',
41063                                     readOnly : 1,
41064                                     name : this.currencyName
41065                                 },
41066                                 {
41067                                     tag :'span',
41068                                     cls : 'input-group-addon',
41069                                     cn : [
41070                                         {
41071                                             tag: 'span',
41072                                             cls: 'caret'
41073                                         }
41074                                     ]
41075                                 }
41076                             ]
41077                         }
41078                     ]
41079                 },
41080                 {
41081                     tag : 'div',
41082                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
41083                     cn : [
41084                         {
41085                             tag: 'div',
41086                             cls: this.hasFeedback ? 'has-feedback' : '',
41087                             cn: [
41088                                 input
41089                             ]
41090                         }
41091                     ]
41092                 }
41093             ]
41094             
41095         };
41096         
41097         if (this.fieldLabel.length) {
41098             var indicator = {
41099                 tag: 'i',
41100                 tooltip: 'This field is required'
41101             };
41102
41103             var label = {
41104                 tag: 'label',
41105                 'for':  id,
41106                 cls: 'control-label',
41107                 cn: []
41108             };
41109
41110             var label_text = {
41111                 tag: 'span',
41112                 html: this.fieldLabel
41113             };
41114
41115             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41116             label.cn = [
41117                 indicator,
41118                 label_text
41119             ];
41120
41121             if(this.indicatorpos == 'right') {
41122                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41123                 label.cn = [
41124                     label_text,
41125                     indicator
41126                 ];
41127             }
41128
41129             if(align == 'left') {
41130                 container = {
41131                     tag: 'div',
41132                     cn: [
41133                         container
41134                     ]
41135                 };
41136
41137                 if(this.labelWidth > 12){
41138                     label.style = "width: " + this.labelWidth + 'px';
41139                 }
41140                 if(this.labelWidth < 13 && this.labelmd == 0){
41141                     this.labelmd = this.labelWidth;
41142                 }
41143                 if(this.labellg > 0){
41144                     label.cls += ' col-lg-' + this.labellg;
41145                     input.cls += ' col-lg-' + (12 - this.labellg);
41146                 }
41147                 if(this.labelmd > 0){
41148                     label.cls += ' col-md-' + this.labelmd;
41149                     container.cls += ' col-md-' + (12 - this.labelmd);
41150                 }
41151                 if(this.labelsm > 0){
41152                     label.cls += ' col-sm-' + this.labelsm;
41153                     container.cls += ' col-sm-' + (12 - this.labelsm);
41154                 }
41155                 if(this.labelxs > 0){
41156                     label.cls += ' col-xs-' + this.labelxs;
41157                     container.cls += ' col-xs-' + (12 - this.labelxs);
41158                 }
41159             }
41160         }
41161
41162         cfg.cn = [
41163             label,
41164             container,
41165             hiddenInput
41166         ];
41167         
41168         var settings = this;
41169
41170         ['xs','sm','md','lg'].map(function(size){
41171             if (settings[size]) {
41172                 cfg.cls += ' col-' + size + '-' + settings[size];
41173             }
41174         });
41175         
41176         return cfg;
41177     },
41178     
41179     initEvents : function()
41180     {
41181         this.indicator = this.indicatorEl();
41182         
41183         this.initCurrencyEvent();
41184         
41185         this.initNumberEvent();
41186     },
41187     
41188     initCurrencyEvent : function()
41189     {
41190         if (!this.store) {
41191             throw "can not find store for combo";
41192         }
41193         
41194         this.store = Roo.factory(this.store, Roo.data);
41195         this.store.parent = this;
41196         
41197         this.createList();
41198         
41199         this.triggerEl = this.el.select('.input-group-addon', true).first();
41200         
41201         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41202         
41203         var _this = this;
41204         
41205         (function(){
41206             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41207             _this.list.setWidth(lw);
41208         }).defer(100);
41209         
41210         this.list.on('mouseover', this.onViewOver, this);
41211         this.list.on('mousemove', this.onViewMove, this);
41212         this.list.on('scroll', this.onViewScroll, this);
41213         
41214         if(!this.tpl){
41215             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41216         }
41217         
41218         this.view = new Roo.View(this.list, this.tpl, {
41219             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41220         });
41221         
41222         this.view.on('click', this.onViewClick, this);
41223         
41224         this.store.on('beforeload', this.onBeforeLoad, this);
41225         this.store.on('load', this.onLoad, this);
41226         this.store.on('loadexception', this.onLoadException, this);
41227         
41228         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41229             "up" : function(e){
41230                 this.inKeyMode = true;
41231                 this.selectPrev();
41232             },
41233
41234             "down" : function(e){
41235                 if(!this.isExpanded()){
41236                     this.onTriggerClick();
41237                 }else{
41238                     this.inKeyMode = true;
41239                     this.selectNext();
41240                 }
41241             },
41242
41243             "enter" : function(e){
41244                 this.collapse();
41245                 
41246                 if(this.fireEvent("specialkey", this, e)){
41247                     this.onViewClick(false);
41248                 }
41249                 
41250                 return true;
41251             },
41252
41253             "esc" : function(e){
41254                 this.collapse();
41255             },
41256
41257             "tab" : function(e){
41258                 this.collapse();
41259                 
41260                 if(this.fireEvent("specialkey", this, e)){
41261                     this.onViewClick(false);
41262                 }
41263                 
41264                 return true;
41265             },
41266
41267             scope : this,
41268
41269             doRelay : function(foo, bar, hname){
41270                 if(hname == 'down' || this.scope.isExpanded()){
41271                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41272                 }
41273                 return true;
41274             },
41275
41276             forceKeyDown: true
41277         });
41278         
41279         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41280         
41281     },
41282     
41283     initNumberEvent : function(e)
41284     {
41285         this.inputEl().on("keydown" , this.fireKey,  this);
41286         this.inputEl().on("focus", this.onFocus,  this);
41287         this.inputEl().on("blur", this.onBlur,  this);
41288         
41289         this.inputEl().relayEvent('keyup', this);
41290         
41291         if(this.indicator){
41292             this.indicator.addClass('invisible');
41293         }
41294  
41295         this.originalValue = this.getValue();
41296         
41297         if(this.validationEvent == 'keyup'){
41298             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41299             this.inputEl().on('keyup', this.filterValidation, this);
41300         }
41301         else if(this.validationEvent !== false){
41302             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41303         }
41304         
41305         if(this.selectOnFocus){
41306             this.on("focus", this.preFocus, this);
41307             
41308         }
41309         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41310             this.inputEl().on("keypress", this.filterKeys, this);
41311         } else {
41312             this.inputEl().relayEvent('keypress', this);
41313         }
41314         
41315         var allowed = "0123456789";
41316         
41317         if(this.allowDecimals){
41318             allowed += this.decimalSeparator;
41319         }
41320         
41321         if(this.allowNegative){
41322             allowed += "-";
41323         }
41324         
41325         if(this.thousandsDelimiter) {
41326             allowed += ",";
41327         }
41328         
41329         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41330         
41331         var keyPress = function(e){
41332             
41333             var k = e.getKey();
41334             
41335             var c = e.getCharCode();
41336             
41337             if(
41338                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41339                     allowed.indexOf(String.fromCharCode(c)) === -1
41340             ){
41341                 e.stopEvent();
41342                 return;
41343             }
41344             
41345             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41346                 return;
41347             }
41348             
41349             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41350                 e.stopEvent();
41351             }
41352         };
41353         
41354         this.inputEl().on("keypress", keyPress, this);
41355         
41356     },
41357     
41358     onTriggerClick : function(e)
41359     {   
41360         if(this.disabled){
41361             return;
41362         }
41363         
41364         this.page = 0;
41365         this.loadNext = false;
41366         
41367         if(this.isExpanded()){
41368             this.collapse();
41369             return;
41370         }
41371         
41372         this.hasFocus = true;
41373         
41374         if(this.triggerAction == 'all') {
41375             this.doQuery(this.allQuery, true);
41376             return;
41377         }
41378         
41379         this.doQuery(this.getRawValue());
41380     },
41381     
41382     getCurrency : function()
41383     {   
41384         var v = this.currencyEl().getValue();
41385         
41386         return v;
41387     },
41388     
41389     restrictHeight : function()
41390     {
41391         this.list.alignTo(this.currencyEl(), this.listAlign);
41392         this.list.alignTo(this.currencyEl(), this.listAlign);
41393     },
41394     
41395     onViewClick : function(view, doFocus, el, e)
41396     {
41397         var index = this.view.getSelectedIndexes()[0];
41398         
41399         var r = this.store.getAt(index);
41400         
41401         if(r){
41402             this.onSelect(r, index);
41403         }
41404     },
41405     
41406     onSelect : function(record, index){
41407         
41408         if(this.fireEvent('beforeselect', this, record, index) !== false){
41409         
41410             this.setFromCurrencyData(index > -1 ? record.data : false);
41411             
41412             this.collapse();
41413             
41414             this.fireEvent('select', this, record, index);
41415         }
41416     },
41417     
41418     setFromCurrencyData : function(o)
41419     {
41420         var currency = '';
41421         
41422         this.lastCurrency = o;
41423         
41424         if (this.currencyField) {
41425             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41426         } else {
41427             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41428         }
41429         
41430         this.lastSelectionText = currency;
41431         
41432         //setting default currency
41433         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41434             this.setCurrency(this.defaultCurrency);
41435             return;
41436         }
41437         
41438         this.setCurrency(currency);
41439     },
41440     
41441     setFromData : function(o)
41442     {
41443         var c = {};
41444         
41445         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41446         
41447         this.setFromCurrencyData(c);
41448         
41449         var value = '';
41450         
41451         if (this.name) {
41452             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41453         } else {
41454             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41455         }
41456         
41457         this.setValue(value);
41458         
41459     },
41460     
41461     setCurrency : function(v)
41462     {   
41463         this.currencyValue = v;
41464         
41465         if(this.rendered){
41466             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41467             this.validate();
41468         }
41469     },
41470     
41471     setValue : function(v)
41472     {
41473         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41474         
41475         this.value = v;
41476         
41477         if(this.rendered){
41478             
41479             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41480             
41481             this.inputEl().dom.value = (v == '') ? '' :
41482                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41483             
41484             if(!this.allowZero && v === '0') {
41485                 this.hiddenEl().dom.value = '';
41486                 this.inputEl().dom.value = '';
41487             }
41488             
41489             this.validate();
41490         }
41491     },
41492     
41493     getRawValue : function()
41494     {
41495         var v = this.inputEl().getValue();
41496         
41497         return v;
41498     },
41499     
41500     getValue : function()
41501     {
41502         return this.fixPrecision(this.parseValue(this.getRawValue()));
41503     },
41504     
41505     parseValue : function(value)
41506     {
41507         if(this.thousandsDelimiter) {
41508             value += "";
41509             r = new RegExp(",", "g");
41510             value = value.replace(r, "");
41511         }
41512         
41513         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41514         return isNaN(value) ? '' : value;
41515         
41516     },
41517     
41518     fixPrecision : function(value)
41519     {
41520         if(this.thousandsDelimiter) {
41521             value += "";
41522             r = new RegExp(",", "g");
41523             value = value.replace(r, "");
41524         }
41525         
41526         var nan = isNaN(value);
41527         
41528         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41529             return nan ? '' : value;
41530         }
41531         return parseFloat(value).toFixed(this.decimalPrecision);
41532     },
41533     
41534     decimalPrecisionFcn : function(v)
41535     {
41536         return Math.floor(v);
41537     },
41538     
41539     validateValue : function(value)
41540     {
41541         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41542             return false;
41543         }
41544         
41545         var num = this.parseValue(value);
41546         
41547         if(isNaN(num)){
41548             this.markInvalid(String.format(this.nanText, value));
41549             return false;
41550         }
41551         
41552         if(num < this.minValue){
41553             this.markInvalid(String.format(this.minText, this.minValue));
41554             return false;
41555         }
41556         
41557         if(num > this.maxValue){
41558             this.markInvalid(String.format(this.maxText, this.maxValue));
41559             return false;
41560         }
41561         
41562         return true;
41563     },
41564     
41565     validate : function()
41566     {
41567         if(this.disabled || this.allowBlank){
41568             this.markValid();
41569             return true;
41570         }
41571         
41572         var currency = this.getCurrency();
41573         
41574         if(this.validateValue(this.getRawValue()) && currency.length){
41575             this.markValid();
41576             return true;
41577         }
41578         
41579         this.markInvalid();
41580         return false;
41581     },
41582     
41583     getName: function()
41584     {
41585         return this.name;
41586     },
41587     
41588     beforeBlur : function()
41589     {
41590         if(!this.castInt){
41591             return;
41592         }
41593         
41594         var v = this.parseValue(this.getRawValue());
41595         
41596         if(v || v == 0){
41597             this.setValue(v);
41598         }
41599     },
41600     
41601     onBlur : function()
41602     {
41603         this.beforeBlur();
41604         
41605         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41606             //this.el.removeClass(this.focusClass);
41607         }
41608         
41609         this.hasFocus = false;
41610         
41611         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41612             this.validate();
41613         }
41614         
41615         var v = this.getValue();
41616         
41617         if(String(v) !== String(this.startValue)){
41618             this.fireEvent('change', this, v, this.startValue);
41619         }
41620         
41621         this.fireEvent("blur", this);
41622     },
41623     
41624     inputEl : function()
41625     {
41626         return this.el.select('.roo-money-amount-input', true).first();
41627     },
41628     
41629     currencyEl : function()
41630     {
41631         return this.el.select('.roo-money-currency-input', true).first();
41632     },
41633     
41634     hiddenEl : function()
41635     {
41636         return this.el.select('input.hidden-number-input',true).first();
41637     }
41638     
41639 });/**
41640  * @class Roo.bootstrap.BezierSignature
41641  * @extends Roo.bootstrap.Component
41642  * Bootstrap BezierSignature class
41643  * This script refer to:
41644  *    Title: Signature Pad
41645  *    Author: szimek
41646  *    Availability: https://github.com/szimek/signature_pad
41647  *
41648  * @constructor
41649  * Create a new BezierSignature
41650  * @param {Object} config The config object
41651  */
41652
41653 Roo.bootstrap.BezierSignature = function(config){
41654     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41655     this.addEvents({
41656         "resize" : true
41657     });
41658 };
41659
41660 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
41661 {
41662      
41663     curve_data: [],
41664     
41665     is_empty: true,
41666     
41667     mouse_btn_down: true,
41668     
41669     /**
41670      * @cfg {int} canvas height
41671      */
41672     canvas_height: '200px',
41673     
41674     /**
41675      * @cfg {float|function} Radius of a single dot.
41676      */ 
41677     dot_size: false,
41678     
41679     /**
41680      * @cfg {float} Minimum width of a line. Defaults to 0.5.
41681      */
41682     min_width: 0.5,
41683     
41684     /**
41685      * @cfg {float} Maximum width of a line. Defaults to 2.5.
41686      */
41687     max_width: 2.5,
41688     
41689     /**
41690      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41691      */
41692     throttle: 16,
41693     
41694     /**
41695      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41696      */
41697     min_distance: 5,
41698     
41699     /**
41700      * @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.
41701      */
41702     bg_color: 'rgba(0, 0, 0, 0)',
41703     
41704     /**
41705      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41706      */
41707     dot_color: 'black',
41708     
41709     /**
41710      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41711      */ 
41712     velocity_filter_weight: 0.7,
41713     
41714     /**
41715      * @cfg {function} Callback when stroke begin. 
41716      */
41717     onBegin: false,
41718     
41719     /**
41720      * @cfg {function} Callback when stroke end.
41721      */
41722     onEnd: false,
41723     
41724     getAutoCreate : function()
41725     {
41726         var cls = 'roo-signature column';
41727         
41728         if(this.cls){
41729             cls += ' ' + this.cls;
41730         }
41731         
41732         var col_sizes = [
41733             'lg',
41734             'md',
41735             'sm',
41736             'xs'
41737         ];
41738         
41739         for(var i = 0; i < col_sizes.length; i++) {
41740             if(this[col_sizes[i]]) {
41741                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
41742             }
41743         }
41744         
41745         var cfg = {
41746             tag: 'div',
41747             cls: cls,
41748             cn: [
41749                 {
41750                     tag: 'div',
41751                     cls: 'roo-signature-body',
41752                     cn: [
41753                         {
41754                             tag: 'canvas',
41755                             cls: 'roo-signature-body-canvas',
41756                             height: this.canvas_height,
41757                             width: this.canvas_width
41758                         }
41759                     ]
41760                 },
41761                 {
41762                     tag: 'input',
41763                     type: 'file',
41764                     style: 'display: none'
41765                 }
41766             ]
41767         };
41768         
41769         return cfg;
41770     },
41771     
41772     initEvents: function() 
41773     {
41774         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
41775         
41776         var canvas = this.canvasEl();
41777         
41778         // mouse && touch event swapping...
41779         canvas.dom.style.touchAction = 'none';
41780         canvas.dom.style.msTouchAction = 'none';
41781         
41782         this.mouse_btn_down = false;
41783         canvas.on('mousedown', this._handleMouseDown, this);
41784         canvas.on('mousemove', this._handleMouseMove, this);
41785         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
41786         
41787         if (window.PointerEvent) {
41788             canvas.on('pointerdown', this._handleMouseDown, this);
41789             canvas.on('pointermove', this._handleMouseMove, this);
41790             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
41791         }
41792         
41793         if ('ontouchstart' in window) {
41794             canvas.on('touchstart', this._handleTouchStart, this);
41795             canvas.on('touchmove', this._handleTouchMove, this);
41796             canvas.on('touchend', this._handleTouchEnd, this);
41797         }
41798         
41799         Roo.EventManager.onWindowResize(this.resize, this, true);
41800         
41801         // file input event
41802         this.fileEl().on('change', this.uploadImage, this);
41803         
41804         this.clear();
41805         
41806         this.resize();
41807     },
41808     
41809     resize: function(){
41810         
41811         var canvas = this.canvasEl().dom;
41812         var ctx = this.canvasElCtx();
41813         var img_data = false;
41814         
41815         if(canvas.width > 0) {
41816             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
41817         }
41818         // setting canvas width will clean img data
41819         canvas.width = 0;
41820         
41821         var style = window.getComputedStyle ? 
41822             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
41823             
41824         var padding_left = parseInt(style.paddingLeft) || 0;
41825         var padding_right = parseInt(style.paddingRight) || 0;
41826         
41827         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
41828         
41829         if(img_data) {
41830             ctx.putImageData(img_data, 0, 0);
41831         }
41832     },
41833     
41834     _handleMouseDown: function(e)
41835     {
41836         if (e.browserEvent.which === 1) {
41837             this.mouse_btn_down = true;
41838             this.strokeBegin(e);
41839         }
41840     },
41841     
41842     _handleMouseMove: function (e)
41843     {
41844         if (this.mouse_btn_down) {
41845             this.strokeMoveUpdate(e);
41846         }
41847     },
41848     
41849     _handleMouseUp: function (e)
41850     {
41851         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
41852             this.mouse_btn_down = false;
41853             this.strokeEnd(e);
41854         }
41855     },
41856     
41857     _handleTouchStart: function (e) {
41858         
41859         e.preventDefault();
41860         if (e.browserEvent.targetTouches.length === 1) {
41861             // var touch = e.browserEvent.changedTouches[0];
41862             // this.strokeBegin(touch);
41863             
41864              this.strokeBegin(e); // assume e catching the correct xy...
41865         }
41866     },
41867     
41868     _handleTouchMove: function (e) {
41869         e.preventDefault();
41870         // var touch = event.targetTouches[0];
41871         // _this._strokeMoveUpdate(touch);
41872         this.strokeMoveUpdate(e);
41873     },
41874     
41875     _handleTouchEnd: function (e) {
41876         var wasCanvasTouched = e.target === this.canvasEl().dom;
41877         if (wasCanvasTouched) {
41878             e.preventDefault();
41879             // var touch = event.changedTouches[0];
41880             // _this._strokeEnd(touch);
41881             this.strokeEnd(e);
41882         }
41883     },
41884     
41885     reset: function () {
41886         this._lastPoints = [];
41887         this._lastVelocity = 0;
41888         this._lastWidth = (this.min_width + this.max_width) / 2;
41889         this.canvasElCtx().fillStyle = this.dot_color;
41890     },
41891     
41892     strokeMoveUpdate: function(e)
41893     {
41894         this.strokeUpdate(e);
41895         
41896         if (this.throttle) {
41897             this.throttleStroke(this.strokeUpdate, this.throttle);
41898         }
41899         else {
41900             this.strokeUpdate(e);
41901         }
41902     },
41903     
41904     strokeBegin: function(e)
41905     {
41906         var newPointGroup = {
41907             color: this.dot_color,
41908             points: []
41909         };
41910         
41911         if (typeof this.onBegin === 'function') {
41912             this.onBegin(e);
41913         }
41914         
41915         this.curve_data.push(newPointGroup);
41916         this.reset();
41917         this.strokeUpdate(e);
41918     },
41919     
41920     strokeUpdate: function(e)
41921     {
41922         var rect = this.canvasEl().dom.getBoundingClientRect();
41923         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
41924         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
41925         var lastPoints = lastPointGroup.points;
41926         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
41927         var isLastPointTooClose = lastPoint
41928             ? point.distanceTo(lastPoint) <= this.min_distance
41929             : false;
41930         var color = lastPointGroup.color;
41931         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
41932             var curve = this.addPoint(point);
41933             if (!lastPoint) {
41934                 this.drawDot({color: color, point: point});
41935             }
41936             else if (curve) {
41937                 this.drawCurve({color: color, curve: curve});
41938             }
41939             lastPoints.push({
41940                 time: point.time,
41941                 x: point.x,
41942                 y: point.y
41943             });
41944         }
41945     },
41946     
41947     strokeEnd: function(e)
41948     {
41949         this.strokeUpdate(e);
41950         if (typeof this.onEnd === 'function') {
41951             this.onEnd(e);
41952         }
41953     },
41954     
41955     addPoint:  function (point) {
41956         var _lastPoints = this._lastPoints;
41957         _lastPoints.push(point);
41958         if (_lastPoints.length > 2) {
41959             if (_lastPoints.length === 3) {
41960                 _lastPoints.unshift(_lastPoints[0]);
41961             }
41962             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
41963             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
41964             _lastPoints.shift();
41965             return curve;
41966         }
41967         return null;
41968     },
41969     
41970     calculateCurveWidths: function (startPoint, endPoint) {
41971         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
41972             (1 - this.velocity_filter_weight) * this._lastVelocity;
41973
41974         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
41975         var widths = {
41976             end: newWidth,
41977             start: this._lastWidth
41978         };
41979         
41980         this._lastVelocity = velocity;
41981         this._lastWidth = newWidth;
41982         return widths;
41983     },
41984     
41985     drawDot: function (_a) {
41986         var color = _a.color, point = _a.point;
41987         var ctx = this.canvasElCtx();
41988         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
41989         ctx.beginPath();
41990         this.drawCurveSegment(point.x, point.y, width);
41991         ctx.closePath();
41992         ctx.fillStyle = color;
41993         ctx.fill();
41994     },
41995     
41996     drawCurve: function (_a) {
41997         var color = _a.color, curve = _a.curve;
41998         var ctx = this.canvasElCtx();
41999         var widthDelta = curve.endWidth - curve.startWidth;
42000         var drawSteps = Math.floor(curve.length()) * 2;
42001         ctx.beginPath();
42002         ctx.fillStyle = color;
42003         for (var i = 0; i < drawSteps; i += 1) {
42004         var t = i / drawSteps;
42005         var tt = t * t;
42006         var ttt = tt * t;
42007         var u = 1 - t;
42008         var uu = u * u;
42009         var uuu = uu * u;
42010         var x = uuu * curve.startPoint.x;
42011         x += 3 * uu * t * curve.control1.x;
42012         x += 3 * u * tt * curve.control2.x;
42013         x += ttt * curve.endPoint.x;
42014         var y = uuu * curve.startPoint.y;
42015         y += 3 * uu * t * curve.control1.y;
42016         y += 3 * u * tt * curve.control2.y;
42017         y += ttt * curve.endPoint.y;
42018         var width = curve.startWidth + ttt * widthDelta;
42019         this.drawCurveSegment(x, y, width);
42020         }
42021         ctx.closePath();
42022         ctx.fill();
42023     },
42024     
42025     drawCurveSegment: function (x, y, width) {
42026         var ctx = this.canvasElCtx();
42027         ctx.moveTo(x, y);
42028         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
42029         this.is_empty = false;
42030     },
42031     
42032     clear: function()
42033     {
42034         var ctx = this.canvasElCtx();
42035         var canvas = this.canvasEl().dom;
42036         ctx.fillStyle = this.bg_color;
42037         ctx.clearRect(0, 0, canvas.width, canvas.height);
42038         ctx.fillRect(0, 0, canvas.width, canvas.height);
42039         this.curve_data = [];
42040         this.reset();
42041         this.is_empty = true;
42042     },
42043     
42044     fileEl: function()
42045     {
42046         return  this.el.select('input',true).first();
42047     },
42048     
42049     canvasEl: function()
42050     {
42051         return this.el.select('canvas',true).first();
42052     },
42053     
42054     canvasElCtx: function()
42055     {
42056         return this.el.select('canvas',true).first().dom.getContext('2d');
42057     },
42058     
42059     getImage: function(type)
42060     {
42061         if(this.is_empty) {
42062             return false;
42063         }
42064         
42065         // encryption ?
42066         return this.canvasEl().dom.toDataURL('image/'+type, 1);
42067     },
42068     
42069     drawFromImage: function(img_src)
42070     {
42071         var img = new Image();
42072         
42073         img.onload = function(){
42074             this.canvasElCtx().drawImage(img, 0, 0);
42075         }.bind(this);
42076         
42077         img.src = img_src;
42078         
42079         this.is_empty = false;
42080     },
42081     
42082     selectImage: function()
42083     {
42084         this.fileEl().dom.click();
42085     },
42086     
42087     uploadImage: function(e)
42088     {
42089         var reader = new FileReader();
42090         
42091         reader.onload = function(e){
42092             var img = new Image();
42093             img.onload = function(){
42094                 this.reset();
42095                 this.canvasElCtx().drawImage(img, 0, 0);
42096             }.bind(this);
42097             img.src = e.target.result;
42098         }.bind(this);
42099         
42100         reader.readAsDataURL(e.target.files[0]);
42101     },
42102     
42103     // Bezier Point Constructor
42104     Point: (function () {
42105         function Point(x, y, time) {
42106             this.x = x;
42107             this.y = y;
42108             this.time = time || Date.now();
42109         }
42110         Point.prototype.distanceTo = function (start) {
42111             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42112         };
42113         Point.prototype.equals = function (other) {
42114             return this.x === other.x && this.y === other.y && this.time === other.time;
42115         };
42116         Point.prototype.velocityFrom = function (start) {
42117             return this.time !== start.time
42118             ? this.distanceTo(start) / (this.time - start.time)
42119             : 0;
42120         };
42121         return Point;
42122     }()),
42123     
42124     
42125     // Bezier Constructor
42126     Bezier: (function () {
42127         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42128             this.startPoint = startPoint;
42129             this.control2 = control2;
42130             this.control1 = control1;
42131             this.endPoint = endPoint;
42132             this.startWidth = startWidth;
42133             this.endWidth = endWidth;
42134         }
42135         Bezier.fromPoints = function (points, widths, scope) {
42136             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42137             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42138             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42139         };
42140         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42141             var dx1 = s1.x - s2.x;
42142             var dy1 = s1.y - s2.y;
42143             var dx2 = s2.x - s3.x;
42144             var dy2 = s2.y - s3.y;
42145             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42146             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42147             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42148             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42149             var dxm = m1.x - m2.x;
42150             var dym = m1.y - m2.y;
42151             var k = l2 / (l1 + l2);
42152             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42153             var tx = s2.x - cm.x;
42154             var ty = s2.y - cm.y;
42155             return {
42156                 c1: new scope.Point(m1.x + tx, m1.y + ty),
42157                 c2: new scope.Point(m2.x + tx, m2.y + ty)
42158             };
42159         };
42160         Bezier.prototype.length = function () {
42161             var steps = 10;
42162             var length = 0;
42163             var px;
42164             var py;
42165             for (var i = 0; i <= steps; i += 1) {
42166                 var t = i / steps;
42167                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42168                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42169                 if (i > 0) {
42170                     var xdiff = cx - px;
42171                     var ydiff = cy - py;
42172                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42173                 }
42174                 px = cx;
42175                 py = cy;
42176             }
42177             return length;
42178         };
42179         Bezier.prototype.point = function (t, start, c1, c2, end) {
42180             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42181             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42182             + (3.0 * c2 * (1.0 - t) * t * t)
42183             + (end * t * t * t);
42184         };
42185         return Bezier;
42186     }()),
42187     
42188     throttleStroke: function(fn, wait) {
42189       if (wait === void 0) { wait = 250; }
42190       var previous = 0;
42191       var timeout = null;
42192       var result;
42193       var storedContext;
42194       var storedArgs;
42195       var later = function () {
42196           previous = Date.now();
42197           timeout = null;
42198           result = fn.apply(storedContext, storedArgs);
42199           if (!timeout) {
42200               storedContext = null;
42201               storedArgs = [];
42202           }
42203       };
42204       return function wrapper() {
42205           var args = [];
42206           for (var _i = 0; _i < arguments.length; _i++) {
42207               args[_i] = arguments[_i];
42208           }
42209           var now = Date.now();
42210           var remaining = wait - (now - previous);
42211           storedContext = this;
42212           storedArgs = args;
42213           if (remaining <= 0 || remaining > wait) {
42214               if (timeout) {
42215                   clearTimeout(timeout);
42216                   timeout = null;
42217               }
42218               previous = now;
42219               result = fn.apply(storedContext, storedArgs);
42220               if (!timeout) {
42221                   storedContext = null;
42222                   storedArgs = [];
42223               }
42224           }
42225           else if (!timeout) {
42226               timeout = window.setTimeout(later, remaining);
42227           }
42228           return result;
42229       };
42230   }
42231   
42232 });
42233
42234  
42235
42236