513b78b9bd90b024b432b46f5cf6c275963eba3d
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets[0], function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })();/*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass(['hidden','d-none']);
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass(['hidden','d-none']);
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * Body
442  *
443  */
444
445 /**
446  * @class Roo.bootstrap.Body
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Body class
449  *
450  * @constructor
451  * Create a new body
452  * @param {Object} config The config object
453  */
454
455 Roo.bootstrap.Body = function(config){
456
457     config = config || {};
458
459     Roo.bootstrap.Body.superclass.constructor.call(this, config);
460     this.el = Roo.get(config.el ? config.el : document.body );
461     if (this.cls && this.cls.length) {
462         Roo.get(document.body).addClass(this.cls);
463     }
464 };
465
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
467
468     is_body : true,// just to make sure it's constructed?
469
470         autoCreate : {
471         cls: 'container'
472     },
473     onRender : function(ct, position)
474     {
475        /* Roo.log("Roo.bootstrap.Body - onRender");
476         if (this.cls && this.cls.length) {
477             Roo.get(document.body).addClass(this.cls);
478         }
479         // style??? xttr???
480         */
481     }
482
483
484
485
486 });
487 /*
488  * - LGPL
489  *
490  * button group
491  * 
492  */
493
494
495 /**
496  * @class Roo.bootstrap.ButtonGroup
497  * @extends Roo.bootstrap.Component
498  * Bootstrap ButtonGroup class
499  * @cfg {String} size lg | sm | xs (default empty normal)
500  * @cfg {String} align vertical | justified  (default none)
501  * @cfg {String} direction up | down (default down)
502  * @cfg {Boolean} toolbar false | true
503  * @cfg {Boolean} btn true | false
504  * 
505  * 
506  * @constructor
507  * Create a new Input
508  * @param {Object} config The config object
509  */
510
511 Roo.bootstrap.ButtonGroup = function(config){
512     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
513 };
514
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
516     
517     size: '',
518     align: '',
519     direction: '',
520     toolbar: false,
521     btn: true,
522
523     getAutoCreate : function(){
524         var cfg = {
525             cls: 'btn-group',
526             html : null
527         };
528         
529         cfg.html = this.html || cfg.html;
530         
531         if (this.toolbar) {
532             cfg = {
533                 cls: 'btn-toolbar',
534                 html: null
535             };
536             
537             return cfg;
538         }
539         
540         if (['vertical','justified'].indexOf(this.align)!==-1) {
541             cfg.cls = 'btn-group-' + this.align;
542             
543             if (this.align == 'justified') {
544                 console.log(this.items);
545             }
546         }
547         
548         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549             cfg.cls += ' btn-group-' + this.size;
550         }
551         
552         if (this.direction == 'up') {
553             cfg.cls += ' dropup' ;
554         }
555         
556         return cfg;
557     },
558     /**
559      * Add a button to the group (similar to NavItem API.)
560      */
561     addItem : function(cfg)
562     {
563         var cn = new Roo.bootstrap.Button(cfg);
564         //this.register(cn);
565         cn.parentId = this.id;
566         cn.onRender(this.el, null);
567         return cn;
568     }
569    
570 });
571
572  /*
573  * - LGPL
574  *
575  * button
576  * 
577  */
578
579 /**
580  * @class Roo.bootstrap.Button
581  * @extends Roo.bootstrap.Component
582  * Bootstrap Button class
583  * @cfg {String} html The button content
584  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587  * @cfg {String} size ( lg | sm | xs)
588  * @cfg {String} tag ( a | input | submit)
589  * @cfg {String} href empty or href
590  * @cfg {Boolean} disabled default false;
591  * @cfg {Boolean} isClose default false;
592  * @cfg {String} glyphicon depricated - use fa
593  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
594  * @cfg {String} badge text for badge
595  * @cfg {String} theme (default|glow)  
596  * @cfg {Boolean} inverse dark themed version
597  * @cfg {Boolean} toggle is it a slidy toggle button
598  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
599  * @cfg {String} ontext text for on slidy toggle state
600  * @cfg {String} offtext text for off slidy toggle state
601  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
602  * @cfg {Boolean} removeClass remove the standard class..
603  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
604  * 
605  * @constructor
606  * Create a new button
607  * @param {Object} config The config object
608  */
609
610
611 Roo.bootstrap.Button = function(config){
612     Roo.bootstrap.Button.superclass.constructor.call(this, config);
613     this.weightClass = ["btn-default btn-outline-secondary", 
614                        "btn-primary", 
615                        "btn-success", 
616                        "btn-info", 
617                        "btn-warning",
618                        "btn-danger",
619                        "btn-link"
620                       ],  
621     this.addEvents({
622         // raw events
623         /**
624          * @event click
625          * When a butotn is pressed
626          * @param {Roo.bootstrap.Button} btn
627          * @param {Roo.EventObject} e
628          */
629         "click" : true,
630          /**
631          * @event toggle
632          * After the button has been toggles
633          * @param {Roo.bootstrap.Button} btn
634          * @param {Roo.EventObject} e
635          * @param {boolean} pressed (also available as button.pressed)
636          */
637         "toggle" : true
638     });
639 };
640
641 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
642     html: false,
643     active: false,
644     weight: '',
645     badge_weight: '',
646     outline : false,
647     size: '',
648     tag: 'button',
649     href: '',
650     disabled: false,
651     isClose: false,
652     glyphicon: '',
653     fa: '',
654     badge: '',
655     theme: 'default',
656     inverse: false,
657     
658     toggle: false,
659     ontext: 'ON',
660     offtext: 'OFF',
661     defaulton: true,
662     preventDefault: true,
663     removeClass: false,
664     name: false,
665     target: false,
666      
667     pressed : null,
668      
669     
670     getAutoCreate : function(){
671         
672         var cfg = {
673             tag : 'button',
674             cls : 'roo-button',
675             html: ''
676         };
677         
678         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
679             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
680             this.tag = 'button';
681         } else {
682             cfg.tag = this.tag;
683         }
684         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
685         
686         if (this.toggle == true) {
687             cfg={
688                 tag: 'div',
689                 cls: 'slider-frame roo-button',
690                 cn: [
691                     {
692                         tag: 'span',
693                         'data-on-text':'ON',
694                         'data-off-text':'OFF',
695                         cls: 'slider-button',
696                         html: this.offtext
697                     }
698                 ]
699             };
700             
701             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
702                 cfg.cls += ' '+this.weight;
703             }
704             
705             return cfg;
706         }
707         
708         if (this.isClose) {
709             cfg.cls += ' close';
710             
711             cfg["aria-hidden"] = true;
712             
713             cfg.html = "&times;";
714             
715             return cfg;
716         }
717         
718          
719         if (this.theme==='default') {
720             cfg.cls = 'btn roo-button';
721             
722             //if (this.parentType != 'Navbar') {
723             this.weight = this.weight.length ?  this.weight : 'default';
724             //}
725             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
726                 
727                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
728                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
729                 cfg.cls += ' btn-' + outline + weight;
730                 if (this.weight == 'default') {
731                     // BC
732                     cfg.cls += ' btn-' + this.weight;
733                 }
734             }
735         } else if (this.theme==='glow') {
736             
737             cfg.tag = 'a';
738             cfg.cls = 'btn-glow roo-button';
739             
740             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
741                 
742                 cfg.cls += ' ' + this.weight;
743             }
744         }
745    
746         
747         if (this.inverse) {
748             this.cls += ' inverse';
749         }
750         
751         
752         if (this.active || this.pressed === true) {
753             cfg.cls += ' active';
754         }
755         
756         if (this.disabled) {
757             cfg.disabled = 'disabled';
758         }
759         
760         if (this.items) {
761             Roo.log('changing to ul' );
762             cfg.tag = 'ul';
763             this.glyphicon = 'caret';
764             if (Roo.bootstrap.version == 4) {
765                 this.fa = 'caret-down';
766             }
767             
768         }
769         
770         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
771          
772         //gsRoo.log(this.parentType);
773         if (this.parentType === 'Navbar' && !this.parent().bar) {
774             Roo.log('changing to li?');
775             
776             cfg.tag = 'li';
777             
778             cfg.cls = '';
779             cfg.cn =  [{
780                 tag : 'a',
781                 cls : 'roo-button',
782                 html : this.html,
783                 href : this.href || '#'
784             }];
785             if (this.menu) {
786                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
787                 cfg.cls += ' dropdown';
788             }   
789             
790             delete cfg.html;
791             
792         }
793         
794        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
795         
796         if (this.glyphicon) {
797             cfg.html = ' ' + cfg.html;
798             
799             cfg.cn = [
800                 {
801                     tag: 'span',
802                     cls: 'glyphicon glyphicon-' + this.glyphicon
803                 }
804             ];
805         }
806         if (this.fa) {
807             cfg.html = ' ' + cfg.html;
808             
809             cfg.cn = [
810                 {
811                     tag: 'i',
812                     cls: 'fa fas fa-' + this.fa
813                 }
814             ];
815         }
816         
817         if (this.badge) {
818             cfg.html += ' ';
819             
820             cfg.tag = 'a';
821             
822 //            cfg.cls='btn roo-button';
823             
824             cfg.href=this.href;
825             
826             var value = cfg.html;
827             
828             if(this.glyphicon){
829                 value = {
830                     tag: 'span',
831                     cls: 'glyphicon glyphicon-' + this.glyphicon,
832                     html: this.html
833                 };
834             }
835             if(this.fa){
836                 value = {
837                     tag: 'i',
838                     cls: 'fa fas fa-' + this.fa,
839                     html: this.html
840                 };
841             }
842             
843             var bw = this.badge_weight.length ? this.badge_weight :
844                 (this.weight.length ? this.weight : 'secondary');
845             bw = bw == 'default' ? 'secondary' : bw;
846             
847             cfg.cn = [
848                 value,
849                 {
850                     tag: 'span',
851                     cls: 'badge badge-' + bw,
852                     html: this.badge
853                 }
854             ];
855             
856             cfg.html='';
857         }
858         
859         if (this.menu) {
860             cfg.cls += ' dropdown';
861             cfg.html = typeof(cfg.html) != 'undefined' ?
862                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
863         }
864         
865         if (cfg.tag !== 'a' && this.href !== '') {
866             throw "Tag must be a to set href.";
867         } else if (this.href.length > 0) {
868             cfg.href = this.href;
869         }
870         
871         if(this.removeClass){
872             cfg.cls = '';
873         }
874         
875         if(this.target){
876             cfg.target = this.target;
877         }
878         
879         return cfg;
880     },
881     initEvents: function() {
882        // Roo.log('init events?');
883 //        Roo.log(this.el.dom);
884         // add the menu...
885         
886         if (typeof (this.menu) != 'undefined') {
887             this.menu.parentType = this.xtype;
888             this.menu.triggerEl = this.el;
889             this.addxtype(Roo.apply({}, this.menu));
890         }
891
892
893        if (this.el.hasClass('roo-button')) {
894             this.el.on('click', this.onClick, this);
895        } else {
896             this.el.select('.roo-button').on('click', this.onClick, this);
897        }
898        
899        if(this.removeClass){
900            this.el.on('click', this.onClick, this);
901        }
902        
903        this.el.enableDisplayMode();
904         
905     },
906     onClick : function(e)
907     {
908         if (this.disabled) {
909             return;
910         }
911         
912         Roo.log('button on click ');
913         if(this.preventDefault){
914             e.preventDefault();
915         }
916         
917         if (this.pressed === true || this.pressed === false) {
918             this.toggleActive(e);
919         }
920         
921         
922         this.fireEvent('click', this, e);
923     },
924     
925     /**
926      * Enables this button
927      */
928     enable : function()
929     {
930         this.disabled = false;
931         this.el.removeClass('disabled');
932     },
933     
934     /**
935      * Disable this button
936      */
937     disable : function()
938     {
939         this.disabled = true;
940         this.el.addClass('disabled');
941     },
942      /**
943      * sets the active state on/off, 
944      * @param {Boolean} state (optional) Force a particular state
945      */
946     setActive : function(v) {
947         
948         this.el[v ? 'addClass' : 'removeClass']('active');
949         this.pressed = v;
950     },
951      /**
952      * toggles the current active state 
953      */
954     toggleActive : function(e)
955     {
956         this.setActive(!this.pressed);
957         this.fireEvent('toggle', this, e, !this.pressed);
958     },
959      /**
960      * get the current active state
961      * @return {boolean} true if it's active
962      */
963     isActive : function()
964     {
965         return this.el.hasClass('active');
966     },
967     /**
968      * set the text of the first selected button
969      */
970     setText : function(str)
971     {
972         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
973     },
974     /**
975      * get the text of the first selected button
976      */
977     getText : function()
978     {
979         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
980     },
981     
982     setWeight : function(str)
983     {
984         this.el.removeClass(this.weightClass);
985         this.weight = str;
986         var outline = this.outline ? 'outline-' : '';
987         if (str == 'default') {
988             this.el.addClass('btn-default btn-outline-secondary');        
989             return;
990         }
991         this.el.addClass('btn-' + outline + str);        
992     }
993     
994     
995 });
996
997  /*
998  * - LGPL
999  *
1000  * column
1001  * 
1002  */
1003
1004 /**
1005  * @class Roo.bootstrap.Column
1006  * @extends Roo.bootstrap.Component
1007  * Bootstrap Column class
1008  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1009  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1010  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1011  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1012  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1013  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1014  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1015  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1016  *
1017  * 
1018  * @cfg {Boolean} hidden (true|false) hide the element
1019  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1020  * @cfg {String} fa (ban|check|...) font awesome icon
1021  * @cfg {Number} fasize (1|2|....) font awsome size
1022
1023  * @cfg {String} icon (info-sign|check|...) glyphicon name
1024
1025  * @cfg {String} html content of column.
1026  * 
1027  * @constructor
1028  * Create a new Column
1029  * @param {Object} config The config object
1030  */
1031
1032 Roo.bootstrap.Column = function(config){
1033     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1034 };
1035
1036 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1037     
1038     xs: false,
1039     sm: false,
1040     md: false,
1041     lg: false,
1042     xsoff: false,
1043     smoff: false,
1044     mdoff: false,
1045     lgoff: false,
1046     html: '',
1047     offset: 0,
1048     alert: false,
1049     fa: false,
1050     icon : false,
1051     hidden : false,
1052     fasize : 1,
1053     
1054     getAutoCreate : function(){
1055         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1056         
1057         cfg = {
1058             tag: 'div',
1059             cls: 'column'
1060         };
1061         
1062         var settings=this;
1063         ['xs','sm','md','lg'].map(function(size){
1064             //Roo.log( size + ':' + settings[size]);
1065             
1066             if (settings[size+'off'] !== false) {
1067                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1068             }
1069             
1070             if (settings[size] === false) {
1071                 return;
1072             }
1073             
1074             if (!settings[size]) { // 0 = hidden
1075                 cfg.cls += ' hidden-' + size;
1076                 return;
1077             }
1078             cfg.cls += ' col-' + size + '-' + settings[size];
1079             
1080         });
1081         
1082         if (this.hidden) {
1083             cfg.cls += ' hidden';
1084         }
1085         
1086         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1087             cfg.cls +=' alert alert-' + this.alert;
1088         }
1089         
1090         
1091         if (this.html.length) {
1092             cfg.html = this.html;
1093         }
1094         if (this.fa) {
1095             var fasize = '';
1096             if (this.fasize > 1) {
1097                 fasize = ' fa-' + this.fasize + 'x';
1098             }
1099             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1100             
1101             
1102         }
1103         if (this.icon) {
1104             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1105         }
1106         
1107         return cfg;
1108     }
1109    
1110 });
1111
1112  
1113
1114  /*
1115  * - LGPL
1116  *
1117  * page container.
1118  * 
1119  */
1120
1121
1122 /**
1123  * @class Roo.bootstrap.Container
1124  * @extends Roo.bootstrap.Component
1125  * Bootstrap Container class
1126  * @cfg {Boolean} jumbotron is it a jumbotron element
1127  * @cfg {String} html content of element
1128  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1129  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1130  * @cfg {String} header content of header (for panel)
1131  * @cfg {String} footer content of footer (for panel)
1132  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1133  * @cfg {String} tag (header|aside|section) type of HTML tag.
1134  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1135  * @cfg {String} fa font awesome icon
1136  * @cfg {String} icon (info-sign|check|...) glyphicon name
1137  * @cfg {Boolean} hidden (true|false) hide the element
1138  * @cfg {Boolean} expandable (true|false) default false
1139  * @cfg {Boolean} expanded (true|false) default true
1140  * @cfg {String} rheader contet on the right of header
1141  * @cfg {Boolean} clickable (true|false) default false
1142
1143  *     
1144  * @constructor
1145  * Create a new Container
1146  * @param {Object} config The config object
1147  */
1148
1149 Roo.bootstrap.Container = function(config){
1150     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1151     
1152     this.addEvents({
1153         // raw events
1154          /**
1155          * @event expand
1156          * After the panel has been expand
1157          * 
1158          * @param {Roo.bootstrap.Container} this
1159          */
1160         "expand" : true,
1161         /**
1162          * @event collapse
1163          * After the panel has been collapsed
1164          * 
1165          * @param {Roo.bootstrap.Container} this
1166          */
1167         "collapse" : true,
1168         /**
1169          * @event click
1170          * When a element is chick
1171          * @param {Roo.bootstrap.Container} this
1172          * @param {Roo.EventObject} e
1173          */
1174         "click" : true
1175     });
1176 };
1177
1178 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1179     
1180     jumbotron : false,
1181     well: '',
1182     panel : '',
1183     header: '',
1184     footer : '',
1185     sticky: '',
1186     tag : false,
1187     alert : false,
1188     fa: false,
1189     icon : false,
1190     expandable : false,
1191     rheader : '',
1192     expanded : true,
1193     clickable: false,
1194   
1195      
1196     getChildContainer : function() {
1197         
1198         if(!this.el){
1199             return false;
1200         }
1201         
1202         if (this.panel.length) {
1203             return this.el.select('.panel-body',true).first();
1204         }
1205         
1206         return this.el;
1207     },
1208     
1209     
1210     getAutoCreate : function(){
1211         
1212         var cfg = {
1213             tag : this.tag || 'div',
1214             html : '',
1215             cls : ''
1216         };
1217         if (this.jumbotron) {
1218             cfg.cls = 'jumbotron';
1219         }
1220         
1221         
1222         
1223         // - this is applied by the parent..
1224         //if (this.cls) {
1225         //    cfg.cls = this.cls + '';
1226         //}
1227         
1228         if (this.sticky.length) {
1229             
1230             var bd = Roo.get(document.body);
1231             if (!bd.hasClass('bootstrap-sticky')) {
1232                 bd.addClass('bootstrap-sticky');
1233                 Roo.select('html',true).setStyle('height', '100%');
1234             }
1235              
1236             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1237         }
1238         
1239         
1240         if (this.well.length) {
1241             switch (this.well) {
1242                 case 'lg':
1243                 case 'sm':
1244                     cfg.cls +=' well well-' +this.well;
1245                     break;
1246                 default:
1247                     cfg.cls +=' well';
1248                     break;
1249             }
1250         }
1251         
1252         if (this.hidden) {
1253             cfg.cls += ' hidden';
1254         }
1255         
1256         
1257         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1258             cfg.cls +=' alert alert-' + this.alert;
1259         }
1260         
1261         var body = cfg;
1262         
1263         if (this.panel.length) {
1264             cfg.cls += ' panel panel-' + this.panel;
1265             cfg.cn = [];
1266             if (this.header.length) {
1267                 
1268                 var h = [];
1269                 
1270                 if(this.expandable){
1271                     
1272                     cfg.cls = cfg.cls + ' expandable';
1273                     
1274                     h.push({
1275                         tag: 'i',
1276                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1277                     });
1278                     
1279                 }
1280                 
1281                 h.push(
1282                     {
1283                         tag: 'span',
1284                         cls : 'panel-title',
1285                         html : (this.expandable ? '&nbsp;' : '') + this.header
1286                     },
1287                     {
1288                         tag: 'span',
1289                         cls: 'panel-header-right',
1290                         html: this.rheader
1291                     }
1292                 );
1293                 
1294                 cfg.cn.push({
1295                     cls : 'panel-heading',
1296                     style : this.expandable ? 'cursor: pointer' : '',
1297                     cn : h
1298                 });
1299                 
1300             }
1301             
1302             body = false;
1303             cfg.cn.push({
1304                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1305                 html : this.html
1306             });
1307             
1308             
1309             if (this.footer.length) {
1310                 cfg.cn.push({
1311                     cls : 'panel-footer',
1312                     html : this.footer
1313                     
1314                 });
1315             }
1316             
1317         }
1318         
1319         if (body) {
1320             body.html = this.html || cfg.html;
1321             // prefix with the icons..
1322             if (this.fa) {
1323                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1324             }
1325             if (this.icon) {
1326                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1327             }
1328             
1329             
1330         }
1331         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1332             cfg.cls =  'container';
1333         }
1334         
1335         return cfg;
1336     },
1337     
1338     initEvents: function() 
1339     {
1340         if(this.expandable){
1341             var headerEl = this.headerEl();
1342         
1343             if(headerEl){
1344                 headerEl.on('click', this.onToggleClick, this);
1345             }
1346         }
1347         
1348         if(this.clickable){
1349             this.el.on('click', this.onClick, this);
1350         }
1351         
1352     },
1353     
1354     onToggleClick : function()
1355     {
1356         var headerEl = this.headerEl();
1357         
1358         if(!headerEl){
1359             return;
1360         }
1361         
1362         if(this.expanded){
1363             this.collapse();
1364             return;
1365         }
1366         
1367         this.expand();
1368     },
1369     
1370     expand : function()
1371     {
1372         if(this.fireEvent('expand', this)) {
1373             
1374             this.expanded = true;
1375             
1376             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1377             
1378             this.el.select('.panel-body',true).first().removeClass('hide');
1379             
1380             var toggleEl = this.toggleEl();
1381
1382             if(!toggleEl){
1383                 return;
1384             }
1385
1386             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1387         }
1388         
1389     },
1390     
1391     collapse : function()
1392     {
1393         if(this.fireEvent('collapse', this)) {
1394             
1395             this.expanded = false;
1396             
1397             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1398             this.el.select('.panel-body',true).first().addClass('hide');
1399         
1400             var toggleEl = this.toggleEl();
1401
1402             if(!toggleEl){
1403                 return;
1404             }
1405
1406             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1407         }
1408     },
1409     
1410     toggleEl : function()
1411     {
1412         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1413             return;
1414         }
1415         
1416         return this.el.select('.panel-heading .fa',true).first();
1417     },
1418     
1419     headerEl : function()
1420     {
1421         if(!this.el || !this.panel.length || !this.header.length){
1422             return;
1423         }
1424         
1425         return this.el.select('.panel-heading',true).first()
1426     },
1427     
1428     bodyEl : function()
1429     {
1430         if(!this.el || !this.panel.length){
1431             return;
1432         }
1433         
1434         return this.el.select('.panel-body',true).first()
1435     },
1436     
1437     titleEl : function()
1438     {
1439         if(!this.el || !this.panel.length || !this.header.length){
1440             return;
1441         }
1442         
1443         return this.el.select('.panel-title',true).first();
1444     },
1445     
1446     setTitle : function(v)
1447     {
1448         var titleEl = this.titleEl();
1449         
1450         if(!titleEl){
1451             return;
1452         }
1453         
1454         titleEl.dom.innerHTML = v;
1455     },
1456     
1457     getTitle : function()
1458     {
1459         
1460         var titleEl = this.titleEl();
1461         
1462         if(!titleEl){
1463             return '';
1464         }
1465         
1466         return titleEl.dom.innerHTML;
1467     },
1468     
1469     setRightTitle : function(v)
1470     {
1471         var t = this.el.select('.panel-header-right',true).first();
1472         
1473         if(!t){
1474             return;
1475         }
1476         
1477         t.dom.innerHTML = v;
1478     },
1479     
1480     onClick : function(e)
1481     {
1482         e.preventDefault();
1483         
1484         this.fireEvent('click', this, e);
1485     }
1486 });
1487
1488  /*
1489  * - LGPL
1490  *
1491  * image
1492  * 
1493  */
1494
1495
1496 /**
1497  * @class Roo.bootstrap.Img
1498  * @extends Roo.bootstrap.Component
1499  * Bootstrap Img class
1500  * @cfg {Boolean} imgResponsive false | true
1501  * @cfg {String} border rounded | circle | thumbnail
1502  * @cfg {String} src image source
1503  * @cfg {String} alt image alternative text
1504  * @cfg {String} href a tag href
1505  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1506  * @cfg {String} xsUrl xs image source
1507  * @cfg {String} smUrl sm image source
1508  * @cfg {String} mdUrl md image source
1509  * @cfg {String} lgUrl lg image source
1510  * 
1511  * @constructor
1512  * Create a new Input
1513  * @param {Object} config The config object
1514  */
1515
1516 Roo.bootstrap.Img = function(config){
1517     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1518     
1519     this.addEvents({
1520         // img events
1521         /**
1522          * @event click
1523          * The img click event for the img.
1524          * @param {Roo.EventObject} e
1525          */
1526         "click" : true
1527     });
1528 };
1529
1530 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1531     
1532     imgResponsive: true,
1533     border: '',
1534     src: 'about:blank',
1535     href: false,
1536     target: false,
1537     xsUrl: '',
1538     smUrl: '',
1539     mdUrl: '',
1540     lgUrl: '',
1541
1542     getAutoCreate : function()
1543     {   
1544         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1545             return this.createSingleImg();
1546         }
1547         
1548         var cfg = {
1549             tag: 'div',
1550             cls: 'roo-image-responsive-group',
1551             cn: []
1552         };
1553         var _this = this;
1554         
1555         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1556             
1557             if(!_this[size + 'Url']){
1558                 return;
1559             }
1560             
1561             var img = {
1562                 tag: 'img',
1563                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1564                 html: _this.html || cfg.html,
1565                 src: _this[size + 'Url']
1566             };
1567             
1568             img.cls += ' roo-image-responsive-' + size;
1569             
1570             var s = ['xs', 'sm', 'md', 'lg'];
1571             
1572             s.splice(s.indexOf(size), 1);
1573             
1574             Roo.each(s, function(ss){
1575                 img.cls += ' hidden-' + ss;
1576             });
1577             
1578             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1579                 cfg.cls += ' img-' + _this.border;
1580             }
1581             
1582             if(_this.alt){
1583                 cfg.alt = _this.alt;
1584             }
1585             
1586             if(_this.href){
1587                 var a = {
1588                     tag: 'a',
1589                     href: _this.href,
1590                     cn: [
1591                         img
1592                     ]
1593                 };
1594
1595                 if(this.target){
1596                     a.target = _this.target;
1597                 }
1598             }
1599             
1600             cfg.cn.push((_this.href) ? a : img);
1601             
1602         });
1603         
1604         return cfg;
1605     },
1606     
1607     createSingleImg : function()
1608     {
1609         var cfg = {
1610             tag: 'img',
1611             cls: (this.imgResponsive) ? 'img-responsive' : '',
1612             html : null,
1613             src : 'about:blank'  // just incase src get's set to undefined?!?
1614         };
1615         
1616         cfg.html = this.html || cfg.html;
1617         
1618         cfg.src = this.src || cfg.src;
1619         
1620         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1621             cfg.cls += ' img-' + this.border;
1622         }
1623         
1624         if(this.alt){
1625             cfg.alt = this.alt;
1626         }
1627         
1628         if(this.href){
1629             var a = {
1630                 tag: 'a',
1631                 href: this.href,
1632                 cn: [
1633                     cfg
1634                 ]
1635             };
1636             
1637             if(this.target){
1638                 a.target = this.target;
1639             }
1640             
1641         }
1642         
1643         return (this.href) ? a : cfg;
1644     },
1645     
1646     initEvents: function() 
1647     {
1648         if(!this.href){
1649             this.el.on('click', this.onClick, this);
1650         }
1651         
1652     },
1653     
1654     onClick : function(e)
1655     {
1656         Roo.log('img onclick');
1657         this.fireEvent('click', this, e);
1658     },
1659     /**
1660      * Sets the url of the image - used to update it
1661      * @param {String} url the url of the image
1662      */
1663     
1664     setSrc : function(url)
1665     {
1666         this.src =  url;
1667         
1668         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1669             this.el.dom.src =  url;
1670             return;
1671         }
1672         
1673         this.el.select('img', true).first().dom.src =  url;
1674     }
1675     
1676     
1677    
1678 });
1679
1680  /*
1681  * - LGPL
1682  *
1683  * image
1684  * 
1685  */
1686
1687
1688 /**
1689  * @class Roo.bootstrap.Link
1690  * @extends Roo.bootstrap.Component
1691  * Bootstrap Link Class
1692  * @cfg {String} alt image alternative text
1693  * @cfg {String} href a tag href
1694  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1695  * @cfg {String} html the content of the link.
1696  * @cfg {String} anchor name for the anchor link
1697  * @cfg {String} fa - favicon
1698
1699  * @cfg {Boolean} preventDefault (true | false) default false
1700
1701  * 
1702  * @constructor
1703  * Create a new Input
1704  * @param {Object} config The config object
1705  */
1706
1707 Roo.bootstrap.Link = function(config){
1708     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1709     
1710     this.addEvents({
1711         // img events
1712         /**
1713          * @event click
1714          * The img click event for the img.
1715          * @param {Roo.EventObject} e
1716          */
1717         "click" : true
1718     });
1719 };
1720
1721 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1722     
1723     href: false,
1724     target: false,
1725     preventDefault: false,
1726     anchor : false,
1727     alt : false,
1728     fa: false,
1729
1730
1731     getAutoCreate : function()
1732     {
1733         var html = this.html || '';
1734         
1735         if (this.fa !== false) {
1736             html = '<i class="fa fa-' + this.fa + '"></i>';
1737         }
1738         var cfg = {
1739             tag: 'a'
1740         };
1741         // anchor's do not require html/href...
1742         if (this.anchor === false) {
1743             cfg.html = html;
1744             cfg.href = this.href || '#';
1745         } else {
1746             cfg.name = this.anchor;
1747             if (this.html !== false || this.fa !== false) {
1748                 cfg.html = html;
1749             }
1750             if (this.href !== false) {
1751                 cfg.href = this.href;
1752             }
1753         }
1754         
1755         if(this.alt !== false){
1756             cfg.alt = this.alt;
1757         }
1758         
1759         
1760         if(this.target !== false) {
1761             cfg.target = this.target;
1762         }
1763         
1764         return cfg;
1765     },
1766     
1767     initEvents: function() {
1768         
1769         if(!this.href || this.preventDefault){
1770             this.el.on('click', this.onClick, this);
1771         }
1772     },
1773     
1774     onClick : function(e)
1775     {
1776         if(this.preventDefault){
1777             e.preventDefault();
1778         }
1779         //Roo.log('img onclick');
1780         this.fireEvent('click', this, e);
1781     }
1782    
1783 });
1784
1785  /*
1786  * - LGPL
1787  *
1788  * header
1789  * 
1790  */
1791
1792 /**
1793  * @class Roo.bootstrap.Header
1794  * @extends Roo.bootstrap.Component
1795  * Bootstrap Header class
1796  * @cfg {String} html content of header
1797  * @cfg {Number} level (1|2|3|4|5|6) default 1
1798  * 
1799  * @constructor
1800  * Create a new Header
1801  * @param {Object} config The config object
1802  */
1803
1804
1805 Roo.bootstrap.Header  = function(config){
1806     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1807 };
1808
1809 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1810     
1811     //href : false,
1812     html : false,
1813     level : 1,
1814     
1815     
1816     
1817     getAutoCreate : function(){
1818         
1819         
1820         
1821         var cfg = {
1822             tag: 'h' + (1 *this.level),
1823             html: this.html || ''
1824         } ;
1825         
1826         return cfg;
1827     }
1828    
1829 });
1830
1831  
1832
1833  /*
1834  * Based on:
1835  * Ext JS Library 1.1.1
1836  * Copyright(c) 2006-2007, Ext JS, LLC.
1837  *
1838  * Originally Released Under LGPL - original licence link has changed is not relivant.
1839  *
1840  * Fork - LGPL
1841  * <script type="text/javascript">
1842  */
1843  
1844 /**
1845  * @class Roo.bootstrap.MenuMgr
1846  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1847  * @singleton
1848  */
1849 Roo.bootstrap.MenuMgr = function(){
1850    var menus, active, groups = {}, attached = false, lastShow = new Date();
1851
1852    // private - called when first menu is created
1853    function init(){
1854        menus = {};
1855        active = new Roo.util.MixedCollection();
1856        Roo.get(document).addKeyListener(27, function(){
1857            if(active.length > 0){
1858                hideAll();
1859            }
1860        });
1861    }
1862
1863    // private
1864    function hideAll(){
1865        if(active && active.length > 0){
1866            var c = active.clone();
1867            c.each(function(m){
1868                m.hide();
1869            });
1870        }
1871    }
1872
1873    // private
1874    function onHide(m){
1875        active.remove(m);
1876        if(active.length < 1){
1877            Roo.get(document).un("mouseup", onMouseDown);
1878             
1879            attached = false;
1880        }
1881    }
1882
1883    // private
1884    function onShow(m){
1885        var last = active.last();
1886        lastShow = new Date();
1887        active.add(m);
1888        if(!attached){
1889           Roo.get(document).on("mouseup", onMouseDown);
1890            
1891            attached = true;
1892        }
1893        if(m.parentMenu){
1894           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1895           m.parentMenu.activeChild = m;
1896        }else if(last && last.isVisible()){
1897           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1898        }
1899    }
1900
1901    // private
1902    function onBeforeHide(m){
1903        if(m.activeChild){
1904            m.activeChild.hide();
1905        }
1906        if(m.autoHideTimer){
1907            clearTimeout(m.autoHideTimer);
1908            delete m.autoHideTimer;
1909        }
1910    }
1911
1912    // private
1913    function onBeforeShow(m){
1914        var pm = m.parentMenu;
1915        if(!pm && !m.allowOtherMenus){
1916            hideAll();
1917        }else if(pm && pm.activeChild && active != m){
1918            pm.activeChild.hide();
1919        }
1920    }
1921
1922    // private this should really trigger on mouseup..
1923    function onMouseDown(e){
1924         Roo.log("on Mouse Up");
1925         
1926         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1927             Roo.log("MenuManager hideAll");
1928             hideAll();
1929             e.stopEvent();
1930         }
1931         
1932         
1933    }
1934
1935    // private
1936    function onBeforeCheck(mi, state){
1937        if(state){
1938            var g = groups[mi.group];
1939            for(var i = 0, l = g.length; i < l; i++){
1940                if(g[i] != mi){
1941                    g[i].setChecked(false);
1942                }
1943            }
1944        }
1945    }
1946
1947    return {
1948
1949        /**
1950         * Hides all menus that are currently visible
1951         */
1952        hideAll : function(){
1953             hideAll();  
1954        },
1955
1956        // private
1957        register : function(menu){
1958            if(!menus){
1959                init();
1960            }
1961            menus[menu.id] = menu;
1962            menu.on("beforehide", onBeforeHide);
1963            menu.on("hide", onHide);
1964            menu.on("beforeshow", onBeforeShow);
1965            menu.on("show", onShow);
1966            var g = menu.group;
1967            if(g && menu.events["checkchange"]){
1968                if(!groups[g]){
1969                    groups[g] = [];
1970                }
1971                groups[g].push(menu);
1972                menu.on("checkchange", onCheck);
1973            }
1974        },
1975
1976         /**
1977          * Returns a {@link Roo.menu.Menu} object
1978          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1979          * be used to generate and return a new Menu instance.
1980          */
1981        get : function(menu){
1982            if(typeof menu == "string"){ // menu id
1983                return menus[menu];
1984            }else if(menu.events){  // menu instance
1985                return menu;
1986            }
1987            /*else if(typeof menu.length == 'number'){ // array of menu items?
1988                return new Roo.bootstrap.Menu({items:menu});
1989            }else{ // otherwise, must be a config
1990                return new Roo.bootstrap.Menu(menu);
1991            }
1992            */
1993            return false;
1994        },
1995
1996        // private
1997        unregister : function(menu){
1998            delete menus[menu.id];
1999            menu.un("beforehide", onBeforeHide);
2000            menu.un("hide", onHide);
2001            menu.un("beforeshow", onBeforeShow);
2002            menu.un("show", onShow);
2003            var g = menu.group;
2004            if(g && menu.events["checkchange"]){
2005                groups[g].remove(menu);
2006                menu.un("checkchange", onCheck);
2007            }
2008        },
2009
2010        // private
2011        registerCheckable : function(menuItem){
2012            var g = menuItem.group;
2013            if(g){
2014                if(!groups[g]){
2015                    groups[g] = [];
2016                }
2017                groups[g].push(menuItem);
2018                menuItem.on("beforecheckchange", onBeforeCheck);
2019            }
2020        },
2021
2022        // private
2023        unregisterCheckable : function(menuItem){
2024            var g = menuItem.group;
2025            if(g){
2026                groups[g].remove(menuItem);
2027                menuItem.un("beforecheckchange", onBeforeCheck);
2028            }
2029        }
2030    };
2031 }();/*
2032  * - LGPL
2033  *
2034  * menu
2035  * 
2036  */
2037
2038 /**
2039  * @class Roo.bootstrap.Menu
2040  * @extends Roo.bootstrap.Component
2041  * Bootstrap Menu class - container for MenuItems
2042  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2043  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2044  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2045  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2046  * 
2047  * @constructor
2048  * Create a new Menu
2049  * @param {Object} config The config object
2050  */
2051
2052
2053 Roo.bootstrap.Menu = function(config){
2054     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2055     if (this.registerMenu && this.type != 'treeview')  {
2056         Roo.bootstrap.MenuMgr.register(this);
2057     }
2058     
2059     
2060     this.addEvents({
2061         /**
2062          * @event beforeshow
2063          * Fires before this menu is displayed (return false to block)
2064          * @param {Roo.menu.Menu} this
2065          */
2066         beforeshow : true,
2067         /**
2068          * @event beforehide
2069          * Fires before this menu is hidden (return false to block)
2070          * @param {Roo.menu.Menu} this
2071          */
2072         beforehide : true,
2073         /**
2074          * @event show
2075          * Fires after this menu is displayed
2076          * @param {Roo.menu.Menu} this
2077          */
2078         show : true,
2079         /**
2080          * @event hide
2081          * Fires after this menu is hidden
2082          * @param {Roo.menu.Menu} this
2083          */
2084         hide : true,
2085         /**
2086          * @event click
2087          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2088          * @param {Roo.menu.Menu} this
2089          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2090          * @param {Roo.EventObject} e
2091          */
2092         click : true,
2093         /**
2094          * @event mouseover
2095          * Fires when the mouse is hovering over this menu
2096          * @param {Roo.menu.Menu} this
2097          * @param {Roo.EventObject} e
2098          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2099          */
2100         mouseover : true,
2101         /**
2102          * @event mouseout
2103          * Fires when the mouse exits this menu
2104          * @param {Roo.menu.Menu} this
2105          * @param {Roo.EventObject} e
2106          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2107          */
2108         mouseout : true,
2109         /**
2110          * @event itemclick
2111          * Fires when a menu item contained in this menu is clicked
2112          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2113          * @param {Roo.EventObject} e
2114          */
2115         itemclick: true
2116     });
2117     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2118 };
2119
2120 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2121     
2122    /// html : false,
2123     //align : '',
2124     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2125     type: false,
2126     /**
2127      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2128      */
2129     registerMenu : true,
2130     
2131     menuItems :false, // stores the menu items..
2132     
2133     hidden:true,
2134         
2135     parentMenu : false,
2136     
2137     stopEvent : true,
2138     
2139     isLink : false,
2140     
2141     getChildContainer : function() {
2142         return this.el;  
2143     },
2144     
2145     getAutoCreate : function(){
2146          
2147         //if (['right'].indexOf(this.align)!==-1) {
2148         //    cfg.cn[1].cls += ' pull-right'
2149         //}
2150         
2151         
2152         var cfg = {
2153             tag : 'ul',
2154             cls : 'dropdown-menu' ,
2155             style : 'z-index:1000'
2156             
2157         };
2158         
2159         if (this.type === 'submenu') {
2160             cfg.cls = 'submenu active';
2161         }
2162         if (this.type === 'treeview') {
2163             cfg.cls = 'treeview-menu';
2164         }
2165         
2166         return cfg;
2167     },
2168     initEvents : function() {
2169         
2170        // Roo.log("ADD event");
2171        // Roo.log(this.triggerEl.dom);
2172         
2173         this.triggerEl.on('click', this.onTriggerClick, this);
2174         
2175         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2176         
2177         
2178         if (this.triggerEl.hasClass('nav-item')) {
2179             // dropdown toggle on the 'a' in BS4?
2180             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2181         } else {
2182             this.triggerEl.addClass('dropdown-toggle');
2183         }
2184         if (Roo.isTouch) {
2185             this.el.on('touchstart'  , this.onTouch, this);
2186         }
2187         this.el.on('click' , this.onClick, this);
2188
2189         this.el.on("mouseover", this.onMouseOver, this);
2190         this.el.on("mouseout", this.onMouseOut, this);
2191         
2192     },
2193     
2194     findTargetItem : function(e)
2195     {
2196         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2197         if(!t){
2198             return false;
2199         }
2200         //Roo.log(t);         Roo.log(t.id);
2201         if(t && t.id){
2202             //Roo.log(this.menuitems);
2203             return this.menuitems.get(t.id);
2204             
2205             //return this.items.get(t.menuItemId);
2206         }
2207         
2208         return false;
2209     },
2210     
2211     onTouch : function(e) 
2212     {
2213         Roo.log("menu.onTouch");
2214         //e.stopEvent(); this make the user popdown broken
2215         this.onClick(e);
2216     },
2217     
2218     onClick : function(e)
2219     {
2220         Roo.log("menu.onClick");
2221         
2222         var t = this.findTargetItem(e);
2223         if(!t || t.isContainer){
2224             return;
2225         }
2226         Roo.log(e);
2227         /*
2228         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2229             if(t == this.activeItem && t.shouldDeactivate(e)){
2230                 this.activeItem.deactivate();
2231                 delete this.activeItem;
2232                 return;
2233             }
2234             if(t.canActivate){
2235                 this.setActiveItem(t, true);
2236             }
2237             return;
2238             
2239             
2240         }
2241         */
2242        
2243         Roo.log('pass click event');
2244         
2245         t.onClick(e);
2246         
2247         this.fireEvent("click", this, t, e);
2248         
2249         var _this = this;
2250         
2251         if(!t.href.length || t.href == '#'){
2252             (function() { _this.hide(); }).defer(100);
2253         }
2254         
2255     },
2256     
2257     onMouseOver : function(e){
2258         var t  = this.findTargetItem(e);
2259         //Roo.log(t);
2260         //if(t){
2261         //    if(t.canActivate && !t.disabled){
2262         //        this.setActiveItem(t, true);
2263         //    }
2264         //}
2265         
2266         this.fireEvent("mouseover", this, e, t);
2267     },
2268     isVisible : function(){
2269         return !this.hidden;
2270     },
2271     onMouseOut : function(e){
2272         var t  = this.findTargetItem(e);
2273         
2274         //if(t ){
2275         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2276         //        this.activeItem.deactivate();
2277         //        delete this.activeItem;
2278         //    }
2279         //}
2280         this.fireEvent("mouseout", this, e, t);
2281     },
2282     
2283     
2284     /**
2285      * Displays this menu relative to another element
2286      * @param {String/HTMLElement/Roo.Element} element The element to align to
2287      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2288      * the element (defaults to this.defaultAlign)
2289      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2290      */
2291     show : function(el, pos, parentMenu)
2292     {
2293         if (false === this.fireEvent("beforeshow", this)) {
2294             Roo.log("show canceled");
2295             return;
2296         }
2297         this.parentMenu = parentMenu;
2298         if(!this.el){
2299             this.render();
2300         }
2301         
2302         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2303     },
2304      /**
2305      * Displays this menu at a specific xy position
2306      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2307      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2308      */
2309     showAt : function(xy, parentMenu, /* private: */_e){
2310         this.parentMenu = parentMenu;
2311         if(!this.el){
2312             this.render();
2313         }
2314         if(_e !== false){
2315             this.fireEvent("beforeshow", this);
2316             //xy = this.el.adjustForConstraints(xy);
2317         }
2318         
2319         //this.el.show();
2320         this.hideMenuItems();
2321         this.hidden = false;
2322         this.triggerEl.addClass('open');
2323         this.el.addClass('show');
2324         
2325         // reassign x when hitting right
2326         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2327             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2328         }
2329         
2330         // reassign y when hitting bottom
2331         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2332             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2333         }
2334         
2335         // but the list may align on trigger left or trigger top... should it be a properity?
2336         
2337         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2338             this.el.setXY(xy);
2339         }
2340         
2341         this.focus();
2342         this.fireEvent("show", this);
2343     },
2344     
2345     focus : function(){
2346         return;
2347         if(!this.hidden){
2348             this.doFocus.defer(50, this);
2349         }
2350     },
2351
2352     doFocus : function(){
2353         if(!this.hidden){
2354             this.focusEl.focus();
2355         }
2356     },
2357
2358     /**
2359      * Hides this menu and optionally all parent menus
2360      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2361      */
2362     hide : function(deep)
2363     {
2364         if (false === this.fireEvent("beforehide", this)) {
2365             Roo.log("hide canceled");
2366             return;
2367         }
2368         this.hideMenuItems();
2369         if(this.el && this.isVisible()){
2370            
2371             if(this.activeItem){
2372                 this.activeItem.deactivate();
2373                 this.activeItem = null;
2374             }
2375             this.triggerEl.removeClass('open');;
2376             this.el.removeClass('show');
2377             this.hidden = true;
2378             this.fireEvent("hide", this);
2379         }
2380         if(deep === true && this.parentMenu){
2381             this.parentMenu.hide(true);
2382         }
2383     },
2384     
2385     onTriggerClick : function(e)
2386     {
2387         Roo.log('trigger click');
2388         
2389         var target = e.getTarget();
2390         
2391         Roo.log(target.nodeName.toLowerCase());
2392         
2393         if(target.nodeName.toLowerCase() === 'i'){
2394             e.preventDefault();
2395         }
2396         
2397     },
2398     
2399     onTriggerPress  : function(e)
2400     {
2401         Roo.log('trigger press');
2402         //Roo.log(e.getTarget());
2403        // Roo.log(this.triggerEl.dom);
2404        
2405         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2406         var pel = Roo.get(e.getTarget());
2407         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2408             Roo.log('is treeview or dropdown?');
2409             return;
2410         }
2411         
2412         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2413             return;
2414         }
2415         
2416         if (this.isVisible()) {
2417             Roo.log('hide');
2418             this.hide();
2419         } else {
2420             Roo.log('show');
2421             this.show(this.triggerEl, '?', false);
2422         }
2423         
2424         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2425             e.stopEvent();
2426         }
2427         
2428     },
2429        
2430     
2431     hideMenuItems : function()
2432     {
2433         Roo.log("hide Menu Items");
2434         if (!this.el) { 
2435             return;
2436         }
2437         
2438         this.el.select('.open',true).each(function(aa) {
2439             
2440             aa.removeClass('open');
2441          
2442         });
2443     },
2444     addxtypeChild : function (tree, cntr) {
2445         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2446           
2447         this.menuitems.add(comp);
2448         return comp;
2449
2450     },
2451     getEl : function()
2452     {
2453         Roo.log(this.el);
2454         return this.el;
2455     },
2456     
2457     clear : function()
2458     {
2459         this.getEl().dom.innerHTML = '';
2460         this.menuitems.clear();
2461     }
2462 });
2463
2464  
2465  /*
2466  * - LGPL
2467  *
2468  * menu item
2469  * 
2470  */
2471
2472
2473 /**
2474  * @class Roo.bootstrap.MenuItem
2475  * @extends Roo.bootstrap.Component
2476  * Bootstrap MenuItem class
2477  * @cfg {String} html the menu label
2478  * @cfg {String} href the link
2479  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2480  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2481  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2482  * @cfg {String} fa favicon to show on left of menu item.
2483  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2484  * 
2485  * 
2486  * @constructor
2487  * Create a new MenuItem
2488  * @param {Object} config The config object
2489  */
2490
2491
2492 Roo.bootstrap.MenuItem = function(config){
2493     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2494     this.addEvents({
2495         // raw events
2496         /**
2497          * @event click
2498          * The raw click event for the entire grid.
2499          * @param {Roo.bootstrap.MenuItem} this
2500          * @param {Roo.EventObject} e
2501          */
2502         "click" : true
2503     });
2504 };
2505
2506 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2507     
2508     href : false,
2509     html : false,
2510     preventDefault: false,
2511     isContainer : false,
2512     active : false,
2513     fa: false,
2514     
2515     getAutoCreate : function(){
2516         
2517         if(this.isContainer){
2518             return {
2519                 tag: 'li',
2520                 cls: 'dropdown-menu-item '
2521             };
2522         }
2523         var ctag = {
2524             tag: 'span',
2525             html: 'Link'
2526         };
2527         
2528         var anc = {
2529             tag : 'a',
2530             cls : 'dropdown-item',
2531             href : '#',
2532             cn : [  ]
2533         };
2534         
2535         if (this.fa !== false) {
2536             anc.cn.push({
2537                 tag : 'i',
2538                 cls : 'fa fa-' + this.fa
2539             });
2540         }
2541         
2542         anc.cn.push(ctag);
2543         
2544         
2545         var cfg= {
2546             tag: 'li',
2547             cls: 'dropdown-menu-item',
2548             cn: [ anc ]
2549         };
2550         if (this.parent().type == 'treeview') {
2551             cfg.cls = 'treeview-menu';
2552         }
2553         if (this.active) {
2554             cfg.cls += ' active';
2555         }
2556         
2557         
2558         
2559         anc.href = this.href || cfg.cn[0].href ;
2560         ctag.html = this.html || cfg.cn[0].html ;
2561         return cfg;
2562     },
2563     
2564     initEvents: function()
2565     {
2566         if (this.parent().type == 'treeview') {
2567             this.el.select('a').on('click', this.onClick, this);
2568         }
2569         
2570         if (this.menu) {
2571             this.menu.parentType = this.xtype;
2572             this.menu.triggerEl = this.el;
2573             this.menu = this.addxtype(Roo.apply({}, this.menu));
2574         }
2575         
2576     },
2577     onClick : function(e)
2578     {
2579         Roo.log('item on click ');
2580         
2581         if(this.preventDefault){
2582             e.preventDefault();
2583         }
2584         //this.parent().hideMenuItems();
2585         
2586         this.fireEvent('click', this, e);
2587     },
2588     getEl : function()
2589     {
2590         return this.el;
2591     } 
2592 });
2593
2594  
2595
2596  /*
2597  * - LGPL
2598  *
2599  * menu separator
2600  * 
2601  */
2602
2603
2604 /**
2605  * @class Roo.bootstrap.MenuSeparator
2606  * @extends Roo.bootstrap.Component
2607  * Bootstrap MenuSeparator class
2608  * 
2609  * @constructor
2610  * Create a new MenuItem
2611  * @param {Object} config The config object
2612  */
2613
2614
2615 Roo.bootstrap.MenuSeparator = function(config){
2616     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2617 };
2618
2619 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2620     
2621     getAutoCreate : function(){
2622         var cfg = {
2623             cls: 'divider',
2624             tag : 'li'
2625         };
2626         
2627         return cfg;
2628     }
2629    
2630 });
2631
2632  
2633
2634  
2635 /*
2636 * Licence: LGPL
2637 */
2638
2639 /**
2640  * @class Roo.bootstrap.Modal
2641  * @extends Roo.bootstrap.Component
2642  * Bootstrap Modal class
2643  * @cfg {String} title Title of dialog
2644  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2645  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2646  * @cfg {Boolean} specificTitle default false
2647  * @cfg {Array} buttons Array of buttons or standard button set..
2648  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2649  * @cfg {Boolean} animate default true
2650  * @cfg {Boolean} allow_close default true
2651  * @cfg {Boolean} fitwindow default false
2652  * @cfg {String} size (sm|lg) default empty
2653  * @cfg {Number} max_width set the max width of modal
2654  *
2655  *
2656  * @constructor
2657  * Create a new Modal Dialog
2658  * @param {Object} config The config object
2659  */
2660
2661 Roo.bootstrap.Modal = function(config){
2662     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2663     this.addEvents({
2664         // raw events
2665         /**
2666          * @event btnclick
2667          * The raw btnclick event for the button
2668          * @param {Roo.EventObject} e
2669          */
2670         "btnclick" : true,
2671         /**
2672          * @event resize
2673          * Fire when dialog resize
2674          * @param {Roo.bootstrap.Modal} this
2675          * @param {Roo.EventObject} e
2676          */
2677         "resize" : true
2678     });
2679     this.buttons = this.buttons || [];
2680
2681     if (this.tmpl) {
2682         this.tmpl = Roo.factory(this.tmpl);
2683     }
2684
2685 };
2686
2687 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2688
2689     title : 'test dialog',
2690
2691     buttons : false,
2692
2693     // set on load...
2694
2695     html: false,
2696
2697     tmp: false,
2698
2699     specificTitle: false,
2700
2701     buttonPosition: 'right',
2702
2703     allow_close : true,
2704
2705     animate : true,
2706
2707     fitwindow: false,
2708     
2709      // private
2710     dialogEl: false,
2711     bodyEl:  false,
2712     footerEl:  false,
2713     titleEl:  false,
2714     closeEl:  false,
2715
2716     size: '',
2717     
2718     max_width: 0,
2719     
2720     max_height: 0,
2721     
2722     fit_content: false,
2723
2724     onRender : function(ct, position)
2725     {
2726         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2727
2728         if(!this.el){
2729             var cfg = Roo.apply({},  this.getAutoCreate());
2730             cfg.id = Roo.id();
2731             //if(!cfg.name){
2732             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2733             //}
2734             //if (!cfg.name.length) {
2735             //    delete cfg.name;
2736            // }
2737             if (this.cls) {
2738                 cfg.cls += ' ' + this.cls;
2739             }
2740             if (this.style) {
2741                 cfg.style = this.style;
2742             }
2743             this.el = Roo.get(document.body).createChild(cfg, position);
2744         }
2745         //var type = this.el.dom.type;
2746
2747
2748         if(this.tabIndex !== undefined){
2749             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2750         }
2751
2752         this.dialogEl = this.el.select('.modal-dialog',true).first();
2753         this.bodyEl = this.el.select('.modal-body',true).first();
2754         this.closeEl = this.el.select('.modal-header .close', true).first();
2755         this.headerEl = this.el.select('.modal-header',true).first();
2756         this.titleEl = this.el.select('.modal-title',true).first();
2757         this.footerEl = this.el.select('.modal-footer',true).first();
2758
2759         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2760         
2761         //this.el.addClass("x-dlg-modal");
2762
2763         if (this.buttons.length) {
2764             Roo.each(this.buttons, function(bb) {
2765                 var b = Roo.apply({}, bb);
2766                 b.xns = b.xns || Roo.bootstrap;
2767                 b.xtype = b.xtype || 'Button';
2768                 if (typeof(b.listeners) == 'undefined') {
2769                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2770                 }
2771
2772                 var btn = Roo.factory(b);
2773
2774                 btn.render(this.getButtonContainer());
2775
2776             },this);
2777         }
2778         // render the children.
2779         var nitems = [];
2780
2781         if(typeof(this.items) != 'undefined'){
2782             var items = this.items;
2783             delete this.items;
2784
2785             for(var i =0;i < items.length;i++) {
2786                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2787             }
2788         }
2789
2790         this.items = nitems;
2791
2792         // where are these used - they used to be body/close/footer
2793
2794
2795         this.initEvents();
2796         //this.el.addClass([this.fieldClass, this.cls]);
2797
2798     },
2799
2800     getAutoCreate : function()
2801     {
2802         var bdy = {
2803                 cls : 'modal-body',
2804                 html : this.html || ''
2805         };
2806
2807         var title = {
2808             tag: 'h4',
2809             cls : 'modal-title',
2810             html : this.title
2811         };
2812
2813         if(this.specificTitle){
2814             title = this.title;
2815
2816         }
2817
2818         var header = [];
2819         if (this.allow_close && Roo.bootstrap.version == 3) {
2820             header.push({
2821                 tag: 'button',
2822                 cls : 'close',
2823                 html : '&times'
2824             });
2825         }
2826
2827         header.push(title);
2828
2829         if (this.allow_close && Roo.bootstrap.version == 4) {
2830             header.push({
2831                 tag: 'button',
2832                 cls : 'close',
2833                 html : '&times'
2834             });
2835         }
2836         
2837         var size = '';
2838
2839         if(this.size.length){
2840             size = 'modal-' + this.size;
2841         }
2842         
2843         var footer = Roo.bootstrap.version == 3 ?
2844             {
2845                 cls : 'modal-footer',
2846                 cn : [
2847                     {
2848                         tag: 'div',
2849                         cls: 'btn-' + this.buttonPosition
2850                     }
2851                 ]
2852
2853             } :
2854             {  // BS4 uses mr-auto on left buttons....
2855                 cls : 'modal-footer'
2856             };
2857
2858             
2859
2860         
2861         
2862         var modal = {
2863             cls: "modal",
2864              cn : [
2865                 {
2866                     cls: "modal-dialog " + size,
2867                     cn : [
2868                         {
2869                             cls : "modal-content",
2870                             cn : [
2871                                 {
2872                                     cls : 'modal-header',
2873                                     cn : header
2874                                 },
2875                                 bdy,
2876                                 footer
2877                             ]
2878
2879                         }
2880                     ]
2881
2882                 }
2883             ]
2884         };
2885
2886         if(this.animate){
2887             modal.cls += ' fade';
2888         }
2889
2890         return modal;
2891
2892     },
2893     getChildContainer : function() {
2894
2895          return this.bodyEl;
2896
2897     },
2898     getButtonContainer : function() {
2899         
2900          return Roo.bootstrap.version == 4 ?
2901             this.el.select('.modal-footer',true).first()
2902             : this.el.select('.modal-footer div',true).first();
2903
2904     },
2905     initEvents : function()
2906     {
2907         if (this.allow_close) {
2908             this.closeEl.on('click', this.hide, this);
2909         }
2910         Roo.EventManager.onWindowResize(this.resize, this, true);
2911
2912
2913     },
2914   
2915
2916     resize : function()
2917     {
2918         this.maskEl.setSize(
2919             Roo.lib.Dom.getViewWidth(true),
2920             Roo.lib.Dom.getViewHeight(true)
2921         );
2922         
2923         if (this.fitwindow) {
2924             
2925            
2926             this.setSize(
2927                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2928                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
2929             );
2930             return;
2931         }
2932         
2933         if(this.max_width !== 0) {
2934             
2935             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2936             
2937             if(this.height) {
2938                 this.setSize(w, this.height);
2939                 return;
2940             }
2941             
2942             if(this.max_height) {
2943                 this.setSize(w,Math.min(
2944                     this.max_height,
2945                     Roo.lib.Dom.getViewportHeight(true) - 60
2946                 ));
2947                 
2948                 return;
2949             }
2950             
2951             if(!this.fit_content) {
2952                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2953                 return;
2954             }
2955             
2956             this.setSize(w, Math.min(
2957                 60 +
2958                 this.headerEl.getHeight() + 
2959                 this.footerEl.getHeight() + 
2960                 this.getChildHeight(this.bodyEl.dom.childNodes),
2961                 Roo.lib.Dom.getViewportHeight(true) - 60)
2962             );
2963         }
2964         
2965     },
2966
2967     setSize : function(w,h)
2968     {
2969         if (!w && !h) {
2970             return;
2971         }
2972         
2973         this.resizeTo(w,h);
2974     },
2975
2976     show : function() {
2977
2978         if (!this.rendered) {
2979             this.render();
2980         }
2981
2982         //this.el.setStyle('display', 'block');
2983         this.el.removeClass('hideing');
2984         this.el.dom.style.display='block';
2985         
2986         Roo.get(document.body).addClass('modal-open');
2987  
2988         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2989             
2990             (function(){
2991                 this.el.addClass('show');
2992                 this.el.addClass('in');
2993             }).defer(50, this);
2994         }else{
2995             this.el.addClass('show');
2996             this.el.addClass('in');
2997         }
2998
2999         // not sure how we can show data in here..
3000         //if (this.tmpl) {
3001         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
3002         //}
3003
3004         Roo.get(document.body).addClass("x-body-masked");
3005         
3006         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3007         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3008         this.maskEl.dom.style.display = 'block';
3009         this.maskEl.addClass('show');
3010         
3011         
3012         this.resize();
3013         
3014         this.fireEvent('show', this);
3015
3016         // set zindex here - otherwise it appears to be ignored...
3017         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3018
3019         (function () {
3020             this.items.forEach( function(e) {
3021                 e.layout ? e.layout() : false;
3022
3023             });
3024         }).defer(100,this);
3025
3026     },
3027     hide : function()
3028     {
3029         if(this.fireEvent("beforehide", this) !== false){
3030             
3031             this.maskEl.removeClass('show');
3032             
3033             this.maskEl.dom.style.display = '';
3034             Roo.get(document.body).removeClass("x-body-masked");
3035             this.el.removeClass('in');
3036             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3037
3038             if(this.animate){ // why
3039                 this.el.addClass('hideing');
3040                 this.el.removeClass('show');
3041                 (function(){
3042                     if (!this.el.hasClass('hideing')) {
3043                         return; // it's been shown again...
3044                     }
3045                     
3046                     this.el.dom.style.display='';
3047
3048                     Roo.get(document.body).removeClass('modal-open');
3049                     this.el.removeClass('hideing');
3050                 }).defer(150,this);
3051                 
3052             }else{
3053                 this.el.removeClass('show');
3054                 this.el.dom.style.display='';
3055                 Roo.get(document.body).removeClass('modal-open');
3056
3057             }
3058             this.fireEvent('hide', this);
3059         }
3060     },
3061     isVisible : function()
3062     {
3063         
3064         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3065         
3066     },
3067
3068     addButton : function(str, cb)
3069     {
3070
3071
3072         var b = Roo.apply({}, { html : str } );
3073         b.xns = b.xns || Roo.bootstrap;
3074         b.xtype = b.xtype || 'Button';
3075         if (typeof(b.listeners) == 'undefined') {
3076             b.listeners = { click : cb.createDelegate(this)  };
3077         }
3078
3079         var btn = Roo.factory(b);
3080
3081         btn.render(this.getButtonContainer());
3082
3083         return btn;
3084
3085     },
3086
3087     setDefaultButton : function(btn)
3088     {
3089         //this.el.select('.modal-footer').()
3090     },
3091
3092     resizeTo: function(w,h)
3093     {
3094         this.dialogEl.setWidth(w);
3095         
3096         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
3097
3098         this.bodyEl.setHeight(h - diff);
3099         
3100         this.fireEvent('resize', this);
3101     },
3102     
3103     setContentSize  : function(w, h)
3104     {
3105
3106     },
3107     onButtonClick: function(btn,e)
3108     {
3109         //Roo.log([a,b,c]);
3110         this.fireEvent('btnclick', btn.name, e);
3111     },
3112      /**
3113      * Set the title of the Dialog
3114      * @param {String} str new Title
3115      */
3116     setTitle: function(str) {
3117         this.titleEl.dom.innerHTML = str;
3118     },
3119     /**
3120      * Set the body of the Dialog
3121      * @param {String} str new Title
3122      */
3123     setBody: function(str) {
3124         this.bodyEl.dom.innerHTML = str;
3125     },
3126     /**
3127      * Set the body of the Dialog using the template
3128      * @param {Obj} data - apply this data to the template and replace the body contents.
3129      */
3130     applyBody: function(obj)
3131     {
3132         if (!this.tmpl) {
3133             Roo.log("Error - using apply Body without a template");
3134             //code
3135         }
3136         this.tmpl.overwrite(this.bodyEl, obj);
3137     },
3138     
3139     getChildHeight : function(child_nodes)
3140     {
3141         if(
3142             !child_nodes ||
3143             child_nodes.length == 0
3144         ) {
3145             return;
3146         }
3147         
3148         var child_height = 0;
3149         
3150         for(var i = 0; i < child_nodes.length; i++) {
3151             
3152             /*
3153             * for modal with tabs...
3154             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3155                 
3156                 var layout_childs = child_nodes[i].childNodes;
3157                 
3158                 for(var j = 0; j < layout_childs.length; j++) {
3159                     
3160                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3161                         
3162                         var layout_body_childs = layout_childs[j].childNodes;
3163                         
3164                         for(var k = 0; k < layout_body_childs.length; k++) {
3165                             
3166                             if(layout_body_childs[k].classList.contains('navbar')) {
3167                                 child_height += layout_body_childs[k].offsetHeight;
3168                                 continue;
3169                             }
3170                             
3171                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3172                                 
3173                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3174                                 
3175                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3176                                     
3177                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3178                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3179                                         continue;
3180                                     }
3181                                     
3182                                 }
3183                                 
3184                             }
3185                             
3186                         }
3187                     }
3188                 }
3189                 continue;
3190             }
3191             */
3192             
3193             child_height += child_nodes[i].offsetHeight;
3194             // Roo.log(child_nodes[i].offsetHeight);
3195         }
3196         
3197         return child_height;
3198     }
3199
3200 });
3201
3202
3203 Roo.apply(Roo.bootstrap.Modal,  {
3204     /**
3205          * Button config that displays a single OK button
3206          * @type Object
3207          */
3208         OK :  [{
3209             name : 'ok',
3210             weight : 'primary',
3211             html : 'OK'
3212         }],
3213         /**
3214          * Button config that displays Yes and No buttons
3215          * @type Object
3216          */
3217         YESNO : [
3218             {
3219                 name  : 'no',
3220                 html : 'No'
3221             },
3222             {
3223                 name  :'yes',
3224                 weight : 'primary',
3225                 html : 'Yes'
3226             }
3227         ],
3228
3229         /**
3230          * Button config that displays OK and Cancel buttons
3231          * @type Object
3232          */
3233         OKCANCEL : [
3234             {
3235                name : 'cancel',
3236                 html : 'Cancel'
3237             },
3238             {
3239                 name : 'ok',
3240                 weight : 'primary',
3241                 html : 'OK'
3242             }
3243         ],
3244         /**
3245          * Button config that displays Yes, No and Cancel buttons
3246          * @type Object
3247          */
3248         YESNOCANCEL : [
3249             {
3250                 name : 'yes',
3251                 weight : 'primary',
3252                 html : 'Yes'
3253             },
3254             {
3255                 name : 'no',
3256                 html : 'No'
3257             },
3258             {
3259                 name : 'cancel',
3260                 html : 'Cancel'
3261             }
3262         ],
3263         
3264         zIndex : 10001
3265 });
3266 /*
3267  * - LGPL
3268  *
3269  * messagebox - can be used as a replace
3270  * 
3271  */
3272 /**
3273  * @class Roo.MessageBox
3274  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3275  * Example usage:
3276  *<pre><code>
3277 // Basic alert:
3278 Roo.Msg.alert('Status', 'Changes saved successfully.');
3279
3280 // Prompt for user data:
3281 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3282     if (btn == 'ok'){
3283         // process text value...
3284     }
3285 });
3286
3287 // Show a dialog using config options:
3288 Roo.Msg.show({
3289    title:'Save Changes?',
3290    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3291    buttons: Roo.Msg.YESNOCANCEL,
3292    fn: processResult,
3293    animEl: 'elId'
3294 });
3295 </code></pre>
3296  * @singleton
3297  */
3298 Roo.bootstrap.MessageBox = function(){
3299     var dlg, opt, mask, waitTimer;
3300     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3301     var buttons, activeTextEl, bwidth;
3302
3303     
3304     // private
3305     var handleButton = function(button){
3306         dlg.hide();
3307         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3308     };
3309
3310     // private
3311     var handleHide = function(){
3312         if(opt && opt.cls){
3313             dlg.el.removeClass(opt.cls);
3314         }
3315         //if(waitTimer){
3316         //    Roo.TaskMgr.stop(waitTimer);
3317         //    waitTimer = null;
3318         //}
3319     };
3320
3321     // private
3322     var updateButtons = function(b){
3323         var width = 0;
3324         if(!b){
3325             buttons["ok"].hide();
3326             buttons["cancel"].hide();
3327             buttons["yes"].hide();
3328             buttons["no"].hide();
3329             dlg.footerEl.hide();
3330             
3331             return width;
3332         }
3333         dlg.footerEl.show();
3334         for(var k in buttons){
3335             if(typeof buttons[k] != "function"){
3336                 if(b[k]){
3337                     buttons[k].show();
3338                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3339                     width += buttons[k].el.getWidth()+15;
3340                 }else{
3341                     buttons[k].hide();
3342                 }
3343             }
3344         }
3345         return width;
3346     };
3347
3348     // private
3349     var handleEsc = function(d, k, e){
3350         if(opt && opt.closable !== false){
3351             dlg.hide();
3352         }
3353         if(e){
3354             e.stopEvent();
3355         }
3356     };
3357
3358     return {
3359         /**
3360          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3361          * @return {Roo.BasicDialog} The BasicDialog element
3362          */
3363         getDialog : function(){
3364            if(!dlg){
3365                 dlg = new Roo.bootstrap.Modal( {
3366                     //draggable: true,
3367                     //resizable:false,
3368                     //constraintoviewport:false,
3369                     //fixedcenter:true,
3370                     //collapsible : false,
3371                     //shim:true,
3372                     //modal: true,
3373                 //    width: 'auto',
3374                   //  height:100,
3375                     //buttonAlign:"center",
3376                     closeClick : function(){
3377                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3378                             handleButton("no");
3379                         }else{
3380                             handleButton("cancel");
3381                         }
3382                     }
3383                 });
3384                 dlg.render();
3385                 dlg.on("hide", handleHide);
3386                 mask = dlg.mask;
3387                 //dlg.addKeyListener(27, handleEsc);
3388                 buttons = {};
3389                 this.buttons = buttons;
3390                 var bt = this.buttonText;
3391                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3392                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3393                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3394                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3395                 //Roo.log(buttons);
3396                 bodyEl = dlg.bodyEl.createChild({
3397
3398                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3399                         '<textarea class="roo-mb-textarea"></textarea>' +
3400                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3401                 });
3402                 msgEl = bodyEl.dom.firstChild;
3403                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3404                 textboxEl.enableDisplayMode();
3405                 textboxEl.addKeyListener([10,13], function(){
3406                     if(dlg.isVisible() && opt && opt.buttons){
3407                         if(opt.buttons.ok){
3408                             handleButton("ok");
3409                         }else if(opt.buttons.yes){
3410                             handleButton("yes");
3411                         }
3412                     }
3413                 });
3414                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3415                 textareaEl.enableDisplayMode();
3416                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3417                 progressEl.enableDisplayMode();
3418                 
3419                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3420                 var pf = progressEl.dom.firstChild;
3421                 if (pf) {
3422                     pp = Roo.get(pf.firstChild);
3423                     pp.setHeight(pf.offsetHeight);
3424                 }
3425                 
3426             }
3427             return dlg;
3428         },
3429
3430         /**
3431          * Updates the message box body text
3432          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3433          * the XHTML-compliant non-breaking space character '&amp;#160;')
3434          * @return {Roo.MessageBox} This message box
3435          */
3436         updateText : function(text)
3437         {
3438             if(!dlg.isVisible() && !opt.width){
3439                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3440                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3441             }
3442             msgEl.innerHTML = text || '&#160;';
3443       
3444             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3445             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3446             var w = Math.max(
3447                     Math.min(opt.width || cw , this.maxWidth), 
3448                     Math.max(opt.minWidth || this.minWidth, bwidth)
3449             );
3450             if(opt.prompt){
3451                 activeTextEl.setWidth(w);
3452             }
3453             if(dlg.isVisible()){
3454                 dlg.fixedcenter = false;
3455             }
3456             // to big, make it scroll. = But as usual stupid IE does not support
3457             // !important..
3458             
3459             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3460                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3461                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3462             } else {
3463                 bodyEl.dom.style.height = '';
3464                 bodyEl.dom.style.overflowY = '';
3465             }
3466             if (cw > w) {
3467                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3468             } else {
3469                 bodyEl.dom.style.overflowX = '';
3470             }
3471             
3472             dlg.setContentSize(w, bodyEl.getHeight());
3473             if(dlg.isVisible()){
3474                 dlg.fixedcenter = true;
3475             }
3476             return this;
3477         },
3478
3479         /**
3480          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3481          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3482          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3483          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3484          * @return {Roo.MessageBox} This message box
3485          */
3486         updateProgress : function(value, text){
3487             if(text){
3488                 this.updateText(text);
3489             }
3490             
3491             if (pp) { // weird bug on my firefox - for some reason this is not defined
3492                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3493                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3494             }
3495             return this;
3496         },        
3497
3498         /**
3499          * Returns true if the message box is currently displayed
3500          * @return {Boolean} True if the message box is visible, else false
3501          */
3502         isVisible : function(){
3503             return dlg && dlg.isVisible();  
3504         },
3505
3506         /**
3507          * Hides the message box if it is displayed
3508          */
3509         hide : function(){
3510             if(this.isVisible()){
3511                 dlg.hide();
3512             }  
3513         },
3514
3515         /**
3516          * Displays a new message box, or reinitializes an existing message box, based on the config options
3517          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3518          * The following config object properties are supported:
3519          * <pre>
3520 Property    Type             Description
3521 ----------  ---------------  ------------------------------------------------------------------------------------
3522 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3523                                    closes (defaults to undefined)
3524 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3525                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3526 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3527                                    progress and wait dialogs will ignore this property and always hide the
3528                                    close button as they can only be closed programmatically.
3529 cls               String           A custom CSS class to apply to the message box element
3530 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3531                                    displayed (defaults to 75)
3532 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3533                                    function will be btn (the name of the button that was clicked, if applicable,
3534                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3535                                    Progress and wait dialogs will ignore this option since they do not respond to
3536                                    user actions and can only be closed programmatically, so any required function
3537                                    should be called by the same code after it closes the dialog.
3538 icon              String           A CSS class that provides a background image to be used as an icon for
3539                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3540 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3541 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3542 modal             Boolean          False to allow user interaction with the page while the message box is
3543                                    displayed (defaults to true)
3544 msg               String           A string that will replace the existing message box body text (defaults
3545                                    to the XHTML-compliant non-breaking space character '&#160;')
3546 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3547 progress          Boolean          True to display a progress bar (defaults to false)
3548 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3549 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3550 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3551 title             String           The title text
3552 value             String           The string value to set into the active textbox element if displayed
3553 wait              Boolean          True to display a progress bar (defaults to false)
3554 width             Number           The width of the dialog in pixels
3555 </pre>
3556          *
3557          * Example usage:
3558          * <pre><code>
3559 Roo.Msg.show({
3560    title: 'Address',
3561    msg: 'Please enter your address:',
3562    width: 300,
3563    buttons: Roo.MessageBox.OKCANCEL,
3564    multiline: true,
3565    fn: saveAddress,
3566    animEl: 'addAddressBtn'
3567 });
3568 </code></pre>
3569          * @param {Object} config Configuration options
3570          * @return {Roo.MessageBox} This message box
3571          */
3572         show : function(options)
3573         {
3574             
3575             // this causes nightmares if you show one dialog after another
3576             // especially on callbacks..
3577              
3578             if(this.isVisible()){
3579                 
3580                 this.hide();
3581                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3582                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3583                 Roo.log("New Dialog Message:" +  options.msg )
3584                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3585                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3586                 
3587             }
3588             var d = this.getDialog();
3589             opt = options;
3590             d.setTitle(opt.title || "&#160;");
3591             d.closeEl.setDisplayed(opt.closable !== false);
3592             activeTextEl = textboxEl;
3593             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3594             if(opt.prompt){
3595                 if(opt.multiline){
3596                     textboxEl.hide();
3597                     textareaEl.show();
3598                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3599                         opt.multiline : this.defaultTextHeight);
3600                     activeTextEl = textareaEl;
3601                 }else{
3602                     textboxEl.show();
3603                     textareaEl.hide();
3604                 }
3605             }else{
3606                 textboxEl.hide();
3607                 textareaEl.hide();
3608             }
3609             progressEl.setDisplayed(opt.progress === true);
3610             if (opt.progress) {
3611                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3612             }
3613             this.updateProgress(0);
3614             activeTextEl.dom.value = opt.value || "";
3615             if(opt.prompt){
3616                 dlg.setDefaultButton(activeTextEl);
3617             }else{
3618                 var bs = opt.buttons;
3619                 var db = null;
3620                 if(bs && bs.ok){
3621                     db = buttons["ok"];
3622                 }else if(bs && bs.yes){
3623                     db = buttons["yes"];
3624                 }
3625                 dlg.setDefaultButton(db);
3626             }
3627             bwidth = updateButtons(opt.buttons);
3628             this.updateText(opt.msg);
3629             if(opt.cls){
3630                 d.el.addClass(opt.cls);
3631             }
3632             d.proxyDrag = opt.proxyDrag === true;
3633             d.modal = opt.modal !== false;
3634             d.mask = opt.modal !== false ? mask : false;
3635             if(!d.isVisible()){
3636                 // force it to the end of the z-index stack so it gets a cursor in FF
3637                 document.body.appendChild(dlg.el.dom);
3638                 d.animateTarget = null;
3639                 d.show(options.animEl);
3640             }
3641             return this;
3642         },
3643
3644         /**
3645          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3646          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3647          * and closing the message box when the process is complete.
3648          * @param {String} title The title bar text
3649          * @param {String} msg The message box body text
3650          * @return {Roo.MessageBox} This message box
3651          */
3652         progress : function(title, msg){
3653             this.show({
3654                 title : title,
3655                 msg : msg,
3656                 buttons: false,
3657                 progress:true,
3658                 closable:false,
3659                 minWidth: this.minProgressWidth,
3660                 modal : true
3661             });
3662             return this;
3663         },
3664
3665         /**
3666          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3667          * If a callback function is passed it will be called after the user clicks the button, and the
3668          * id of the button that was clicked will be passed as the only parameter to the callback
3669          * (could also be the top-right close button).
3670          * @param {String} title The title bar text
3671          * @param {String} msg The message box body text
3672          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3673          * @param {Object} scope (optional) The scope of the callback function
3674          * @return {Roo.MessageBox} This message box
3675          */
3676         alert : function(title, msg, fn, scope)
3677         {
3678             this.show({
3679                 title : title,
3680                 msg : msg,
3681                 buttons: this.OK,
3682                 fn: fn,
3683                 closable : false,
3684                 scope : scope,
3685                 modal : true
3686             });
3687             return this;
3688         },
3689
3690         /**
3691          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3692          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3693          * You are responsible for closing the message box when the process is complete.
3694          * @param {String} msg The message box body text
3695          * @param {String} title (optional) The title bar text
3696          * @return {Roo.MessageBox} This message box
3697          */
3698         wait : function(msg, title){
3699             this.show({
3700                 title : title,
3701                 msg : msg,
3702                 buttons: false,
3703                 closable:false,
3704                 progress:true,
3705                 modal:true,
3706                 width:300,
3707                 wait:true
3708             });
3709             waitTimer = Roo.TaskMgr.start({
3710                 run: function(i){
3711                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3712                 },
3713                 interval: 1000
3714             });
3715             return this;
3716         },
3717
3718         /**
3719          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3720          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3721          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3722          * @param {String} title The title bar text
3723          * @param {String} msg The message box body text
3724          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3725          * @param {Object} scope (optional) The scope of the callback function
3726          * @return {Roo.MessageBox} This message box
3727          */
3728         confirm : function(title, msg, fn, scope){
3729             this.show({
3730                 title : title,
3731                 msg : msg,
3732                 buttons: this.YESNO,
3733                 fn: fn,
3734                 scope : scope,
3735                 modal : true
3736             });
3737             return this;
3738         },
3739
3740         /**
3741          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3742          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3743          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3744          * (could also be the top-right close button) and the text that was entered will be passed as the two
3745          * parameters to the callback.
3746          * @param {String} title The title bar text
3747          * @param {String} msg The message box body text
3748          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3749          * @param {Object} scope (optional) The scope of the callback function
3750          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3751          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3752          * @return {Roo.MessageBox} This message box
3753          */
3754         prompt : function(title, msg, fn, scope, multiline){
3755             this.show({
3756                 title : title,
3757                 msg : msg,
3758                 buttons: this.OKCANCEL,
3759                 fn: fn,
3760                 minWidth:250,
3761                 scope : scope,
3762                 prompt:true,
3763                 multiline: multiline,
3764                 modal : true
3765             });
3766             return this;
3767         },
3768
3769         /**
3770          * Button config that displays a single OK button
3771          * @type Object
3772          */
3773         OK : {ok:true},
3774         /**
3775          * Button config that displays Yes and No buttons
3776          * @type Object
3777          */
3778         YESNO : {yes:true, no:true},
3779         /**
3780          * Button config that displays OK and Cancel buttons
3781          * @type Object
3782          */
3783         OKCANCEL : {ok:true, cancel:true},
3784         /**
3785          * Button config that displays Yes, No and Cancel buttons
3786          * @type Object
3787          */
3788         YESNOCANCEL : {yes:true, no:true, cancel:true},
3789
3790         /**
3791          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3792          * @type Number
3793          */
3794         defaultTextHeight : 75,
3795         /**
3796          * The maximum width in pixels of the message box (defaults to 600)
3797          * @type Number
3798          */
3799         maxWidth : 600,
3800         /**
3801          * The minimum width in pixels of the message box (defaults to 100)
3802          * @type Number
3803          */
3804         minWidth : 100,
3805         /**
3806          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3807          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3808          * @type Number
3809          */
3810         minProgressWidth : 250,
3811         /**
3812          * An object containing the default button text strings that can be overriden for localized language support.
3813          * Supported properties are: ok, cancel, yes and no.
3814          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3815          * @type Object
3816          */
3817         buttonText : {
3818             ok : "OK",
3819             cancel : "Cancel",
3820             yes : "Yes",
3821             no : "No"
3822         }
3823     };
3824 }();
3825
3826 /**
3827  * Shorthand for {@link Roo.MessageBox}
3828  */
3829 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3830 Roo.Msg = Roo.Msg || Roo.MessageBox;
3831 /*
3832  * - LGPL
3833  *
3834  * navbar
3835  * 
3836  */
3837
3838 /**
3839  * @class Roo.bootstrap.Navbar
3840  * @extends Roo.bootstrap.Component
3841  * Bootstrap Navbar class
3842
3843  * @constructor
3844  * Create a new Navbar
3845  * @param {Object} config The config object
3846  */
3847
3848
3849 Roo.bootstrap.Navbar = function(config){
3850     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3851     this.addEvents({
3852         // raw events
3853         /**
3854          * @event beforetoggle
3855          * Fire before toggle the menu
3856          * @param {Roo.EventObject} e
3857          */
3858         "beforetoggle" : true
3859     });
3860 };
3861
3862 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3863     
3864     
3865    
3866     // private
3867     navItems : false,
3868     loadMask : false,
3869     
3870     
3871     getAutoCreate : function(){
3872         
3873         
3874         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3875         
3876     },
3877     
3878     initEvents :function ()
3879     {
3880         //Roo.log(this.el.select('.navbar-toggle',true));
3881         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
3882         
3883         var mark = {
3884             tag: "div",
3885             cls:"x-dlg-mask"
3886         };
3887         
3888         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3889         
3890         var size = this.el.getSize();
3891         this.maskEl.setSize(size.width, size.height);
3892         this.maskEl.enableDisplayMode("block");
3893         this.maskEl.hide();
3894         
3895         if(this.loadMask){
3896             this.maskEl.show();
3897         }
3898     },
3899     
3900     
3901     getChildContainer : function()
3902     {
3903         if (this.el && this.el.select('.collapse').getCount()) {
3904             return this.el.select('.collapse',true).first();
3905         }
3906         
3907         return this.el;
3908     },
3909     
3910     mask : function()
3911     {
3912         this.maskEl.show();
3913     },
3914     
3915     unmask : function()
3916     {
3917         this.maskEl.hide();
3918     },
3919     onToggle : function()
3920     {
3921         
3922         if(this.fireEvent('beforetoggle', this) === false){
3923             return;
3924         }
3925         var ce = this.el.select('.navbar-collapse',true).first();
3926       
3927         if (!ce.hasClass('show')) {
3928            this.expand();
3929         } else {
3930             this.collapse();
3931         }
3932         
3933         
3934     
3935     },
3936     /**
3937      * Expand the navbar pulldown 
3938      */
3939     expand : function ()
3940     {
3941        
3942         var ce = this.el.select('.navbar-collapse',true).first();
3943         if (ce.hasClass('collapsing')) {
3944             return;
3945         }
3946         ce.dom.style.height = '';
3947                // show it...
3948         ce.addClass('in'); // old...
3949         ce.removeClass('collapse');
3950         ce.addClass('show');
3951         var h = ce.getHeight();
3952         Roo.log(h);
3953         ce.removeClass('show');
3954         // at this point we should be able to see it..
3955         ce.addClass('collapsing');
3956         
3957         ce.setHeight(0); // resize it ...
3958         ce.on('transitionend', function() {
3959             //Roo.log('done transition');
3960             ce.removeClass('collapsing');
3961             ce.addClass('show');
3962             ce.removeClass('collapse');
3963
3964             ce.dom.style.height = '';
3965         }, this, { single: true} );
3966         ce.setHeight(h);
3967         ce.dom.scrollTop = 0;
3968     },
3969     /**
3970      * Collapse the navbar pulldown 
3971      */
3972     collapse : function()
3973     {
3974          var ce = this.el.select('.navbar-collapse',true).first();
3975        
3976         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
3977             // it's collapsed or collapsing..
3978             return;
3979         }
3980         ce.removeClass('in'); // old...
3981         ce.setHeight(ce.getHeight());
3982         ce.removeClass('show');
3983         ce.addClass('collapsing');
3984         
3985         ce.on('transitionend', function() {
3986             ce.dom.style.height = '';
3987             ce.removeClass('collapsing');
3988             ce.addClass('collapse');
3989         }, this, { single: true} );
3990         ce.setHeight(0);
3991     }
3992     
3993     
3994     
3995 });
3996
3997
3998
3999  
4000
4001  /*
4002  * - LGPL
4003  *
4004  * navbar
4005  * 
4006  */
4007
4008 /**
4009  * @class Roo.bootstrap.NavSimplebar
4010  * @extends Roo.bootstrap.Navbar
4011  * Bootstrap Sidebar class
4012  *
4013  * @cfg {Boolean} inverse is inverted color
4014  * 
4015  * @cfg {String} type (nav | pills | tabs)
4016  * @cfg {Boolean} arrangement stacked | justified
4017  * @cfg {String} align (left | right) alignment
4018  * 
4019  * @cfg {Boolean} main (true|false) main nav bar? default false
4020  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
4021  * 
4022  * @cfg {String} tag (header|footer|nav|div) default is nav 
4023
4024  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
4025  * 
4026  * 
4027  * @constructor
4028  * Create a new Sidebar
4029  * @param {Object} config The config object
4030  */
4031
4032
4033 Roo.bootstrap.NavSimplebar = function(config){
4034     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
4035 };
4036
4037 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4038     
4039     inverse: false,
4040     
4041     type: false,
4042     arrangement: '',
4043     align : false,
4044     
4045     weight : 'light',
4046     
4047     main : false,
4048     
4049     
4050     tag : false,
4051     
4052     
4053     getAutoCreate : function(){
4054         
4055         
4056         var cfg = {
4057             tag : this.tag || 'div',
4058             cls : 'navbar navbar-expand-lg roo-navbar-simple'
4059         };
4060         if (['light','white'].indexOf(this.weight) > -1) {
4061             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4062         }
4063         cfg.cls += ' bg-' + this.weight;
4064         
4065         if (this.inverse) {
4066             cfg.cls += ' navbar-inverse';
4067             
4068         }
4069         
4070         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4071         
4072         //if (Roo.bootstrap.version == 4) {
4073         //    return cfg;
4074         //}
4075         
4076         cfg.cn = [
4077             {
4078                 cls: 'nav',
4079                 tag : 'ul'
4080             }
4081         ];
4082         
4083          
4084         this.type = this.type || 'nav';
4085         if (['tabs','pills'].indexOf(this.type) != -1) {
4086             cfg.cn[0].cls += ' nav-' + this.type
4087         
4088         
4089         } else {
4090             if (this.type!=='nav') {
4091                 Roo.log('nav type must be nav/tabs/pills')
4092             }
4093             cfg.cn[0].cls += ' navbar-nav'
4094         }
4095         
4096         
4097         
4098         
4099         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4100             cfg.cn[0].cls += ' nav-' + this.arrangement;
4101         }
4102         
4103         
4104         if (this.align === 'right') {
4105             cfg.cn[0].cls += ' navbar-right';
4106         }
4107         
4108         
4109         
4110         
4111         return cfg;
4112     
4113         
4114     }
4115     
4116     
4117     
4118 });
4119
4120
4121
4122  
4123
4124  
4125        /*
4126  * - LGPL
4127  *
4128  * navbar
4129  * navbar-fixed-top
4130  * navbar-expand-md  fixed-top 
4131  */
4132
4133 /**
4134  * @class Roo.bootstrap.NavHeaderbar
4135  * @extends Roo.bootstrap.NavSimplebar
4136  * Bootstrap Sidebar class
4137  *
4138  * @cfg {String} brand what is brand
4139  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4140  * @cfg {String} brand_href href of the brand
4141  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4142  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4143  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4144  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4145  * 
4146  * @constructor
4147  * Create a new Sidebar
4148  * @param {Object} config The config object
4149  */
4150
4151
4152 Roo.bootstrap.NavHeaderbar = function(config){
4153     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4154       
4155 };
4156
4157 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4158     
4159     position: '',
4160     brand: '',
4161     brand_href: false,
4162     srButton : true,
4163     autohide : false,
4164     desktopCenter : false,
4165    
4166     
4167     getAutoCreate : function(){
4168         
4169         var   cfg = {
4170             tag: this.nav || 'nav',
4171             cls: 'navbar navbar-expand-md',
4172             role: 'navigation',
4173             cn: []
4174         };
4175         
4176         var cn = cfg.cn;
4177         if (this.desktopCenter) {
4178             cn.push({cls : 'container', cn : []});
4179             cn = cn[0].cn;
4180         }
4181         
4182         if(this.srButton){
4183             var btn = {
4184                 tag: 'button',
4185                 type: 'button',
4186                 cls: 'navbar-toggle navbar-toggler',
4187                 'data-toggle': 'collapse',
4188                 cn: [
4189                     {
4190                         tag: 'span',
4191                         cls: 'sr-only',
4192                         html: 'Toggle navigation'
4193                     },
4194                     {
4195                         tag: 'span',
4196                         cls: 'icon-bar navbar-toggler-icon'
4197                     },
4198                     {
4199                         tag: 'span',
4200                         cls: 'icon-bar'
4201                     },
4202                     {
4203                         tag: 'span',
4204                         cls: 'icon-bar'
4205                     }
4206                 ]
4207             };
4208             
4209             cn.push( Roo.bootstrap.version == 4 ? btn : {
4210                 tag: 'div',
4211                 cls: 'navbar-header',
4212                 cn: [
4213                     btn
4214                 ]
4215             });
4216         }
4217         
4218         cn.push({
4219             tag: 'div',
4220             cls: 'collapse navbar-collapse',
4221             cn : []
4222         });
4223         
4224         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4225         
4226         if (['light','white'].indexOf(this.weight) > -1) {
4227             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4228         }
4229         cfg.cls += ' bg-' + this.weight;
4230         
4231         
4232         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4233             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4234             
4235             // tag can override this..
4236             
4237             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4238         }
4239         
4240         if (this.brand !== '') {
4241             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4242             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4243                 tag: 'a',
4244                 href: this.brand_href ? this.brand_href : '#',
4245                 cls: 'navbar-brand',
4246                 cn: [
4247                 this.brand
4248                 ]
4249             });
4250         }
4251         
4252         if(this.main){
4253             cfg.cls += ' main-nav';
4254         }
4255         
4256         
4257         return cfg;
4258
4259         
4260     },
4261     getHeaderChildContainer : function()
4262     {
4263         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4264             return this.el.select('.navbar-header',true).first();
4265         }
4266         
4267         return this.getChildContainer();
4268     },
4269     
4270     
4271     initEvents : function()
4272     {
4273         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4274         
4275         if (this.autohide) {
4276             
4277             var prevScroll = 0;
4278             var ft = this.el;
4279             
4280             Roo.get(document).on('scroll',function(e) {
4281                 var ns = Roo.get(document).getScroll().top;
4282                 var os = prevScroll;
4283                 prevScroll = ns;
4284                 
4285                 if(ns > os){
4286                     ft.removeClass('slideDown');
4287                     ft.addClass('slideUp');
4288                     return;
4289                 }
4290                 ft.removeClass('slideUp');
4291                 ft.addClass('slideDown');
4292                  
4293               
4294           },this);
4295         }
4296     }    
4297     
4298 });
4299
4300
4301
4302  
4303
4304  /*
4305  * - LGPL
4306  *
4307  * navbar
4308  * 
4309  */
4310
4311 /**
4312  * @class Roo.bootstrap.NavSidebar
4313  * @extends Roo.bootstrap.Navbar
4314  * Bootstrap Sidebar class
4315  * 
4316  * @constructor
4317  * Create a new Sidebar
4318  * @param {Object} config The config object
4319  */
4320
4321
4322 Roo.bootstrap.NavSidebar = function(config){
4323     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4324 };
4325
4326 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4327     
4328     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4329     
4330     getAutoCreate : function(){
4331         
4332         
4333         return  {
4334             tag: 'div',
4335             cls: 'sidebar sidebar-nav'
4336         };
4337     
4338         
4339     }
4340     
4341     
4342     
4343 });
4344
4345
4346
4347  
4348
4349  /*
4350  * - LGPL
4351  *
4352  * nav group
4353  * 
4354  */
4355
4356 /**
4357  * @class Roo.bootstrap.NavGroup
4358  * @extends Roo.bootstrap.Component
4359  * Bootstrap NavGroup class
4360  * @cfg {String} align (left|right)
4361  * @cfg {Boolean} inverse
4362  * @cfg {String} type (nav|pills|tab) default nav
4363  * @cfg {String} navId - reference Id for navbar.
4364
4365  * 
4366  * @constructor
4367  * Create a new nav group
4368  * @param {Object} config The config object
4369  */
4370
4371 Roo.bootstrap.NavGroup = function(config){
4372     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4373     this.navItems = [];
4374    
4375     Roo.bootstrap.NavGroup.register(this);
4376      this.addEvents({
4377         /**
4378              * @event changed
4379              * Fires when the active item changes
4380              * @param {Roo.bootstrap.NavGroup} this
4381              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4382              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4383          */
4384         'changed': true
4385      });
4386     
4387 };
4388
4389 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4390     
4391     align: '',
4392     inverse: false,
4393     form: false,
4394     type: 'nav',
4395     navId : '',
4396     // private
4397     
4398     navItems : false, 
4399     
4400     getAutoCreate : function()
4401     {
4402         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4403         
4404         cfg = {
4405             tag : 'ul',
4406             cls: 'nav' 
4407         };
4408         if (Roo.bootstrap.version == 4) {
4409             if (['tabs','pills'].indexOf(this.type) != -1) {
4410                 cfg.cls += ' nav-' + this.type; 
4411             } else {
4412                 cfg.cls += ' navbar-nav';
4413             }
4414         } else {
4415             if (['tabs','pills'].indexOf(this.type) != -1) {
4416                 cfg.cls += ' nav-' + this.type
4417             } else {
4418                 if (this.type !== 'nav') {
4419                     Roo.log('nav type must be nav/tabs/pills')
4420                 }
4421                 cfg.cls += ' navbar-nav'
4422             }
4423         }
4424         
4425         if (this.parent() && this.parent().sidebar) {
4426             cfg = {
4427                 tag: 'ul',
4428                 cls: 'dashboard-menu sidebar-menu'
4429             };
4430             
4431             return cfg;
4432         }
4433         
4434         if (this.form === true) {
4435             cfg = {
4436                 tag: 'form',
4437                 cls: 'navbar-form form-inline'
4438             };
4439             
4440             if (this.align === 'right') {
4441                 cfg.cls += ' navbar-right ml-md-auto';
4442             } else {
4443                 cfg.cls += ' navbar-left';
4444             }
4445         }
4446         
4447         if (this.align === 'right') {
4448             cfg.cls += ' navbar-right ml-md-auto';
4449         } else {
4450             cfg.cls += ' mr-auto';
4451         }
4452         
4453         if (this.inverse) {
4454             cfg.cls += ' navbar-inverse';
4455             
4456         }
4457         
4458         
4459         return cfg;
4460     },
4461     /**
4462     * sets the active Navigation item
4463     * @param {Roo.bootstrap.NavItem} the new current navitem
4464     */
4465     setActiveItem : function(item)
4466     {
4467         var prev = false;
4468         Roo.each(this.navItems, function(v){
4469             if (v == item) {
4470                 return ;
4471             }
4472             if (v.isActive()) {
4473                 v.setActive(false, true);
4474                 prev = v;
4475                 
4476             }
4477             
4478         });
4479
4480         item.setActive(true, true);
4481         this.fireEvent('changed', this, item, prev);
4482         
4483         
4484     },
4485     /**
4486     * gets the active Navigation item
4487     * @return {Roo.bootstrap.NavItem} the current navitem
4488     */
4489     getActive : function()
4490     {
4491         
4492         var prev = false;
4493         Roo.each(this.navItems, function(v){
4494             
4495             if (v.isActive()) {
4496                 prev = v;
4497                 
4498             }
4499             
4500         });
4501         return prev;
4502     },
4503     
4504     indexOfNav : function()
4505     {
4506         
4507         var prev = false;
4508         Roo.each(this.navItems, function(v,i){
4509             
4510             if (v.isActive()) {
4511                 prev = i;
4512                 
4513             }
4514             
4515         });
4516         return prev;
4517     },
4518     /**
4519     * adds a Navigation item
4520     * @param {Roo.bootstrap.NavItem} the navitem to add
4521     */
4522     addItem : function(cfg)
4523     {
4524         if (this.form && Roo.bootstrap.version == 4) {
4525             cfg.tag = 'div';
4526         }
4527         var cn = new Roo.bootstrap.NavItem(cfg);
4528         this.register(cn);
4529         cn.parentId = this.id;
4530         cn.onRender(this.el, null);
4531         return cn;
4532     },
4533     /**
4534     * register a Navigation item
4535     * @param {Roo.bootstrap.NavItem} the navitem to add
4536     */
4537     register : function(item)
4538     {
4539         this.navItems.push( item);
4540         item.navId = this.navId;
4541     
4542     },
4543     
4544     /**
4545     * clear all the Navigation item
4546     */
4547    
4548     clearAll : function()
4549     {
4550         this.navItems = [];
4551         this.el.dom.innerHTML = '';
4552     },
4553     
4554     getNavItem: function(tabId)
4555     {
4556         var ret = false;
4557         Roo.each(this.navItems, function(e) {
4558             if (e.tabId == tabId) {
4559                ret =  e;
4560                return false;
4561             }
4562             return true;
4563             
4564         });
4565         return ret;
4566     },
4567     
4568     setActiveNext : function()
4569     {
4570         var i = this.indexOfNav(this.getActive());
4571         if (i > this.navItems.length) {
4572             return;
4573         }
4574         this.setActiveItem(this.navItems[i+1]);
4575     },
4576     setActivePrev : function()
4577     {
4578         var i = this.indexOfNav(this.getActive());
4579         if (i  < 1) {
4580             return;
4581         }
4582         this.setActiveItem(this.navItems[i-1]);
4583     },
4584     clearWasActive : function(except) {
4585         Roo.each(this.navItems, function(e) {
4586             if (e.tabId != except.tabId && e.was_active) {
4587                e.was_active = false;
4588                return false;
4589             }
4590             return true;
4591             
4592         });
4593     },
4594     getWasActive : function ()
4595     {
4596         var r = false;
4597         Roo.each(this.navItems, function(e) {
4598             if (e.was_active) {
4599                r = e;
4600                return false;
4601             }
4602             return true;
4603             
4604         });
4605         return r;
4606     }
4607     
4608     
4609 });
4610
4611  
4612 Roo.apply(Roo.bootstrap.NavGroup, {
4613     
4614     groups: {},
4615      /**
4616     * register a Navigation Group
4617     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4618     */
4619     register : function(navgrp)
4620     {
4621         this.groups[navgrp.navId] = navgrp;
4622         
4623     },
4624     /**
4625     * fetch a Navigation Group based on the navigation ID
4626     * @param {string} the navgroup to add
4627     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4628     */
4629     get: function(navId) {
4630         if (typeof(this.groups[navId]) == 'undefined') {
4631             return false;
4632             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4633         }
4634         return this.groups[navId] ;
4635     }
4636     
4637     
4638     
4639 });
4640
4641  /*
4642  * - LGPL
4643  *
4644  * row
4645  * 
4646  */
4647
4648 /**
4649  * @class Roo.bootstrap.NavItem
4650  * @extends Roo.bootstrap.Component
4651  * Bootstrap Navbar.NavItem class
4652  * @cfg {String} href  link to
4653  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4654
4655  * @cfg {String} html content of button
4656  * @cfg {String} badge text inside badge
4657  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4658  * @cfg {String} glyphicon DEPRICATED - use fa
4659  * @cfg {String} icon DEPRICATED - use fa
4660  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4661  * @cfg {Boolean} active Is item active
4662  * @cfg {Boolean} disabled Is item disabled
4663  
4664  * @cfg {Boolean} preventDefault (true | false) default false
4665  * @cfg {String} tabId the tab that this item activates.
4666  * @cfg {String} tagtype (a|span) render as a href or span?
4667  * @cfg {Boolean} animateRef (true|false) link to element default false  
4668   
4669  * @constructor
4670  * Create a new Navbar Item
4671  * @param {Object} config The config object
4672  */
4673 Roo.bootstrap.NavItem = function(config){
4674     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4675     this.addEvents({
4676         // raw events
4677         /**
4678          * @event click
4679          * The raw click event for the entire grid.
4680          * @param {Roo.EventObject} e
4681          */
4682         "click" : true,
4683          /**
4684             * @event changed
4685             * Fires when the active item active state changes
4686             * @param {Roo.bootstrap.NavItem} this
4687             * @param {boolean} state the new state
4688              
4689          */
4690         'changed': true,
4691         /**
4692             * @event scrollto
4693             * Fires when scroll to element
4694             * @param {Roo.bootstrap.NavItem} this
4695             * @param {Object} options
4696             * @param {Roo.EventObject} e
4697              
4698          */
4699         'scrollto': true
4700     });
4701    
4702 };
4703
4704 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4705     
4706     href: false,
4707     html: '',
4708     badge: '',
4709     icon: false,
4710     fa : false,
4711     glyphicon: false,
4712     active: false,
4713     preventDefault : false,
4714     tabId : false,
4715     tagtype : 'a',
4716     tag: 'li',
4717     disabled : false,
4718     animateRef : false,
4719     was_active : false,
4720     button_weight : '',
4721     button_outline : false,
4722     
4723     navLink: false,
4724     
4725     getAutoCreate : function(){
4726          
4727         var cfg = {
4728             tag: this.tag,
4729             cls: 'nav-item'
4730         };
4731         
4732         if (this.active) {
4733             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4734         }
4735         if (this.disabled) {
4736             cfg.cls += ' disabled';
4737         }
4738         
4739         // BS4 only?
4740         if (this.button_weight.length) {
4741             cfg.tag = this.href ? 'a' : 'button';
4742             cfg.html = this.html || '';
4743             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4744             if (this.href) {
4745                 cfg.href = this.href;
4746             }
4747             if (this.fa) {
4748                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
4749             }
4750             
4751             // menu .. should add dropdown-menu class - so no need for carat..
4752             
4753             if (this.badge !== '') {
4754                  
4755                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4756             }
4757             return cfg;
4758         }
4759         
4760         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4761             cfg.cn = [
4762                 {
4763                     tag: this.tagtype,
4764                     href : this.href || "#",
4765                     html: this.html || ''
4766                 }
4767             ];
4768             if (this.tagtype == 'a') {
4769                 cfg.cn[0].cls = 'nav-link';
4770             }
4771             if (this.icon) {
4772                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
4773             }
4774             if (this.fa) {
4775                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
4776             }
4777             if(this.glyphicon) {
4778                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4779             }
4780             
4781             if (this.menu) {
4782                 
4783                 cfg.cn[0].html += " <span class='caret'></span>";
4784              
4785             }
4786             
4787             if (this.badge !== '') {
4788                  
4789                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4790             }
4791         }
4792         
4793         
4794         
4795         return cfg;
4796     },
4797     onRender : function(ct, position)
4798     {
4799        // Roo.log("Call onRender: " + this.xtype);
4800         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4801             this.tag = 'div';
4802         }
4803         
4804         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4805         this.navLink = this.el.select('.nav-link',true).first();
4806         return ret;
4807     },
4808       
4809     
4810     initEvents: function() 
4811     {
4812         if (typeof (this.menu) != 'undefined') {
4813             this.menu.parentType = this.xtype;
4814             this.menu.triggerEl = this.el;
4815             this.menu = this.addxtype(Roo.apply({}, this.menu));
4816         }
4817         
4818         this.el.select('a',true).on('click', this.onClick, this);
4819         
4820         if(this.tagtype == 'span'){
4821             this.el.select('span',true).on('click', this.onClick, this);
4822         }
4823        
4824         // at this point parent should be available..
4825         this.parent().register(this);
4826     },
4827     
4828     onClick : function(e)
4829     {
4830         if (e.getTarget('.dropdown-menu-item')) {
4831             // did you click on a menu itemm.... - then don't trigger onclick..
4832             return;
4833         }
4834         
4835         if(
4836                 this.preventDefault || 
4837                 this.href == '#' 
4838         ){
4839             Roo.log("NavItem - prevent Default?");
4840             e.preventDefault();
4841         }
4842         
4843         if (this.disabled) {
4844             return;
4845         }
4846         
4847         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4848         if (tg && tg.transition) {
4849             Roo.log("waiting for the transitionend");
4850             return;
4851         }
4852         
4853         
4854         
4855         //Roo.log("fire event clicked");
4856         if(this.fireEvent('click', this, e) === false){
4857             return;
4858         };
4859         
4860         if(this.tagtype == 'span'){
4861             return;
4862         }
4863         
4864         //Roo.log(this.href);
4865         var ael = this.el.select('a',true).first();
4866         //Roo.log(ael);
4867         
4868         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4869             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4870             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4871                 return; // ignore... - it's a 'hash' to another page.
4872             }
4873             Roo.log("NavItem - prevent Default?");
4874             e.preventDefault();
4875             this.scrollToElement(e);
4876         }
4877         
4878         
4879         var p =  this.parent();
4880    
4881         if (['tabs','pills'].indexOf(p.type)!==-1) {
4882             if (typeof(p.setActiveItem) !== 'undefined') {
4883                 p.setActiveItem(this);
4884             }
4885         }
4886         
4887         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4888         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4889             // remove the collapsed menu expand...
4890             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4891         }
4892     },
4893     
4894     isActive: function () {
4895         return this.active
4896     },
4897     setActive : function(state, fire, is_was_active)
4898     {
4899         if (this.active && !state && this.navId) {
4900             this.was_active = true;
4901             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4902             if (nv) {
4903                 nv.clearWasActive(this);
4904             }
4905             
4906         }
4907         this.active = state;
4908         
4909         if (!state ) {
4910             this.el.removeClass('active');
4911             this.navLink ? this.navLink.removeClass('active') : false;
4912         } else if (!this.el.hasClass('active')) {
4913             
4914             this.el.addClass('active');
4915             if (Roo.bootstrap.version == 4 && this.navLink ) {
4916                 this.navLink.addClass('active');
4917             }
4918             
4919         }
4920         if (fire) {
4921             this.fireEvent('changed', this, state);
4922         }
4923         
4924         // show a panel if it's registered and related..
4925         
4926         if (!this.navId || !this.tabId || !state || is_was_active) {
4927             return;
4928         }
4929         
4930         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4931         if (!tg) {
4932             return;
4933         }
4934         var pan = tg.getPanelByName(this.tabId);
4935         if (!pan) {
4936             return;
4937         }
4938         // if we can not flip to new panel - go back to old nav highlight..
4939         if (false == tg.showPanel(pan)) {
4940             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4941             if (nv) {
4942                 var onav = nv.getWasActive();
4943                 if (onav) {
4944                     onav.setActive(true, false, true);
4945                 }
4946             }
4947             
4948         }
4949         
4950         
4951         
4952     },
4953      // this should not be here...
4954     setDisabled : function(state)
4955     {
4956         this.disabled = state;
4957         if (!state ) {
4958             this.el.removeClass('disabled');
4959         } else if (!this.el.hasClass('disabled')) {
4960             this.el.addClass('disabled');
4961         }
4962         
4963     },
4964     
4965     /**
4966      * Fetch the element to display the tooltip on.
4967      * @return {Roo.Element} defaults to this.el
4968      */
4969     tooltipEl : function()
4970     {
4971         return this.el.select('' + this.tagtype + '', true).first();
4972     },
4973     
4974     scrollToElement : function(e)
4975     {
4976         var c = document.body;
4977         
4978         /*
4979          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4980          */
4981         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4982             c = document.documentElement;
4983         }
4984         
4985         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4986         
4987         if(!target){
4988             return;
4989         }
4990
4991         var o = target.calcOffsetsTo(c);
4992         
4993         var options = {
4994             target : target,
4995             value : o[1]
4996         };
4997         
4998         this.fireEvent('scrollto', this, options, e);
4999         
5000         Roo.get(c).scrollTo('top', options.value, true);
5001         
5002         return;
5003     }
5004 });
5005  
5006
5007  /*
5008  * - LGPL
5009  *
5010  * sidebar item
5011  *
5012  *  li
5013  *    <span> icon </span>
5014  *    <span> text </span>
5015  *    <span>badge </span>
5016  */
5017
5018 /**
5019  * @class Roo.bootstrap.NavSidebarItem
5020  * @extends Roo.bootstrap.NavItem
5021  * Bootstrap Navbar.NavSidebarItem class
5022  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
5023  * {Boolean} open is the menu open
5024  * {Boolean} buttonView use button as the tigger el rather that a (default false)
5025  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
5026  * {String} buttonSize (sm|md|lg)the extra classes for the button
5027  * {Boolean} showArrow show arrow next to the text (default true)
5028  * @constructor
5029  * Create a new Navbar Button
5030  * @param {Object} config The config object
5031  */
5032 Roo.bootstrap.NavSidebarItem = function(config){
5033     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
5034     this.addEvents({
5035         // raw events
5036         /**
5037          * @event click
5038          * The raw click event for the entire grid.
5039          * @param {Roo.EventObject} e
5040          */
5041         "click" : true,
5042          /**
5043             * @event changed
5044             * Fires when the active item active state changes
5045             * @param {Roo.bootstrap.NavSidebarItem} this
5046             * @param {boolean} state the new state
5047              
5048          */
5049         'changed': true
5050     });
5051    
5052 };
5053
5054 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
5055     
5056     badgeWeight : 'default',
5057     
5058     open: false,
5059     
5060     buttonView : false,
5061     
5062     buttonWeight : 'default',
5063     
5064     buttonSize : 'md',
5065     
5066     showArrow : true,
5067     
5068     getAutoCreate : function(){
5069         
5070         
5071         var a = {
5072                 tag: 'a',
5073                 href : this.href || '#',
5074                 cls: '',
5075                 html : '',
5076                 cn : []
5077         };
5078         
5079         if(this.buttonView){
5080             a = {
5081                 tag: 'button',
5082                 href : this.href || '#',
5083                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5084                 html : this.html,
5085                 cn : []
5086             };
5087         }
5088         
5089         var cfg = {
5090             tag: 'li',
5091             cls: '',
5092             cn: [ a ]
5093         };
5094         
5095         if (this.active) {
5096             cfg.cls += ' active';
5097         }
5098         
5099         if (this.disabled) {
5100             cfg.cls += ' disabled';
5101         }
5102         if (this.open) {
5103             cfg.cls += ' open x-open';
5104         }
5105         // left icon..
5106         if (this.glyphicon || this.icon) {
5107             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5108             a.cn.push({ tag : 'i', cls : c }) ;
5109         }
5110         
5111         if(!this.buttonView){
5112             var span = {
5113                 tag: 'span',
5114                 html : this.html || ''
5115             };
5116
5117             a.cn.push(span);
5118             
5119         }
5120         
5121         if (this.badge !== '') {
5122             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5123         }
5124         
5125         if (this.menu) {
5126             
5127             if(this.showArrow){
5128                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5129             }
5130             
5131             a.cls += ' dropdown-toggle treeview' ;
5132         }
5133         
5134         return cfg;
5135     },
5136     
5137     initEvents : function()
5138     { 
5139         if (typeof (this.menu) != 'undefined') {
5140             this.menu.parentType = this.xtype;
5141             this.menu.triggerEl = this.el;
5142             this.menu = this.addxtype(Roo.apply({}, this.menu));
5143         }
5144         
5145         this.el.on('click', this.onClick, this);
5146         
5147         if(this.badge !== ''){
5148             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5149         }
5150         
5151     },
5152     
5153     onClick : function(e)
5154     {
5155         if(this.disabled){
5156             e.preventDefault();
5157             return;
5158         }
5159         
5160         if(this.preventDefault){
5161             e.preventDefault();
5162         }
5163         
5164         this.fireEvent('click', this, e);
5165     },
5166     
5167     disable : function()
5168     {
5169         this.setDisabled(true);
5170     },
5171     
5172     enable : function()
5173     {
5174         this.setDisabled(false);
5175     },
5176     
5177     setDisabled : function(state)
5178     {
5179         if(this.disabled == state){
5180             return;
5181         }
5182         
5183         this.disabled = state;
5184         
5185         if (state) {
5186             this.el.addClass('disabled');
5187             return;
5188         }
5189         
5190         this.el.removeClass('disabled');
5191         
5192         return;
5193     },
5194     
5195     setActive : function(state)
5196     {
5197         if(this.active == state){
5198             return;
5199         }
5200         
5201         this.active = state;
5202         
5203         if (state) {
5204             this.el.addClass('active');
5205             return;
5206         }
5207         
5208         this.el.removeClass('active');
5209         
5210         return;
5211     },
5212     
5213     isActive: function () 
5214     {
5215         return this.active;
5216     },
5217     
5218     setBadge : function(str)
5219     {
5220         if(!this.badgeEl){
5221             return;
5222         }
5223         
5224         this.badgeEl.dom.innerHTML = str;
5225     }
5226     
5227    
5228      
5229  
5230 });
5231  
5232
5233  /*
5234  * - LGPL
5235  *
5236  * row
5237  * 
5238  */
5239
5240 /**
5241  * @class Roo.bootstrap.Row
5242  * @extends Roo.bootstrap.Component
5243  * Bootstrap Row class (contains columns...)
5244  * 
5245  * @constructor
5246  * Create a new Row
5247  * @param {Object} config The config object
5248  */
5249
5250 Roo.bootstrap.Row = function(config){
5251     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5252 };
5253
5254 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5255     
5256     getAutoCreate : function(){
5257        return {
5258             cls: 'row clearfix'
5259        };
5260     }
5261     
5262     
5263 });
5264
5265  
5266
5267  /*
5268  * - LGPL
5269  *
5270  * element
5271  * 
5272  */
5273
5274 /**
5275  * @class Roo.bootstrap.Element
5276  * @extends Roo.bootstrap.Component
5277  * Bootstrap Element class
5278  * @cfg {String} html contents of the element
5279  * @cfg {String} tag tag of the element
5280  * @cfg {String} cls class of the element
5281  * @cfg {Boolean} preventDefault (true|false) default false
5282  * @cfg {Boolean} clickable (true|false) default false
5283  * 
5284  * @constructor
5285  * Create a new Element
5286  * @param {Object} config The config object
5287  */
5288
5289 Roo.bootstrap.Element = function(config){
5290     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5291     
5292     this.addEvents({
5293         // raw events
5294         /**
5295          * @event click
5296          * When a element is chick
5297          * @param {Roo.bootstrap.Element} this
5298          * @param {Roo.EventObject} e
5299          */
5300         "click" : true
5301     });
5302 };
5303
5304 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5305     
5306     tag: 'div',
5307     cls: '',
5308     html: '',
5309     preventDefault: false, 
5310     clickable: false,
5311     
5312     getAutoCreate : function(){
5313         
5314         var cfg = {
5315             tag: this.tag,
5316             // cls: this.cls, double assign in parent class Component.js :: onRender
5317             html: this.html
5318         };
5319         
5320         return cfg;
5321     },
5322     
5323     initEvents: function() 
5324     {
5325         Roo.bootstrap.Element.superclass.initEvents.call(this);
5326         
5327         if(this.clickable){
5328             this.el.on('click', this.onClick, this);
5329         }
5330         
5331     },
5332     
5333     onClick : function(e)
5334     {
5335         if(this.preventDefault){
5336             e.preventDefault();
5337         }
5338         
5339         this.fireEvent('click', this, e);
5340     },
5341     
5342     getValue : function()
5343     {
5344         return this.el.dom.innerHTML;
5345     },
5346     
5347     setValue : function(value)
5348     {
5349         this.el.dom.innerHTML = value;
5350     }
5351    
5352 });
5353
5354  
5355
5356  /*
5357  * - LGPL
5358  *
5359  * pagination
5360  * 
5361  */
5362
5363 /**
5364  * @class Roo.bootstrap.Pagination
5365  * @extends Roo.bootstrap.Component
5366  * Bootstrap Pagination class
5367  * @cfg {String} size xs | sm | md | lg
5368  * @cfg {Boolean} inverse false | true
5369  * 
5370  * @constructor
5371  * Create a new Pagination
5372  * @param {Object} config The config object
5373  */
5374
5375 Roo.bootstrap.Pagination = function(config){
5376     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5377 };
5378
5379 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5380     
5381     cls: false,
5382     size: false,
5383     inverse: false,
5384     
5385     getAutoCreate : function(){
5386         var cfg = {
5387             tag: 'ul',
5388                 cls: 'pagination'
5389         };
5390         if (this.inverse) {
5391             cfg.cls += ' inverse';
5392         }
5393         if (this.html) {
5394             cfg.html=this.html;
5395         }
5396         if (this.cls) {
5397             cfg.cls += " " + this.cls;
5398         }
5399         return cfg;
5400     }
5401    
5402 });
5403
5404  
5405
5406  /*
5407  * - LGPL
5408  *
5409  * Pagination item
5410  * 
5411  */
5412
5413
5414 /**
5415  * @class Roo.bootstrap.PaginationItem
5416  * @extends Roo.bootstrap.Component
5417  * Bootstrap PaginationItem class
5418  * @cfg {String} html text
5419  * @cfg {String} href the link
5420  * @cfg {Boolean} preventDefault (true | false) default true
5421  * @cfg {Boolean} active (true | false) default false
5422  * @cfg {Boolean} disabled default false
5423  * 
5424  * 
5425  * @constructor
5426  * Create a new PaginationItem
5427  * @param {Object} config The config object
5428  */
5429
5430
5431 Roo.bootstrap.PaginationItem = function(config){
5432     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5433     this.addEvents({
5434         // raw events
5435         /**
5436          * @event click
5437          * The raw click event for the entire grid.
5438          * @param {Roo.EventObject} e
5439          */
5440         "click" : true
5441     });
5442 };
5443
5444 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5445     
5446     href : false,
5447     html : false,
5448     preventDefault: true,
5449     active : false,
5450     cls : false,
5451     disabled: false,
5452     
5453     getAutoCreate : function(){
5454         var cfg= {
5455             tag: 'li',
5456             cn: [
5457                 {
5458                     tag : 'a',
5459                     href : this.href ? this.href : '#',
5460                     html : this.html ? this.html : ''
5461                 }
5462             ]
5463         };
5464         
5465         if(this.cls){
5466             cfg.cls = this.cls;
5467         }
5468         
5469         if(this.disabled){
5470             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5471         }
5472         
5473         if(this.active){
5474             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5475         }
5476         
5477         return cfg;
5478     },
5479     
5480     initEvents: function() {
5481         
5482         this.el.on('click', this.onClick, this);
5483         
5484     },
5485     onClick : function(e)
5486     {
5487         Roo.log('PaginationItem on click ');
5488         if(this.preventDefault){
5489             e.preventDefault();
5490         }
5491         
5492         if(this.disabled){
5493             return;
5494         }
5495         
5496         this.fireEvent('click', this, e);
5497     }
5498    
5499 });
5500
5501  
5502
5503  /*
5504  * - LGPL
5505  *
5506  * slider
5507  * 
5508  */
5509
5510
5511 /**
5512  * @class Roo.bootstrap.Slider
5513  * @extends Roo.bootstrap.Component
5514  * Bootstrap Slider class
5515  *    
5516  * @constructor
5517  * Create a new Slider
5518  * @param {Object} config The config object
5519  */
5520
5521 Roo.bootstrap.Slider = function(config){
5522     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5523 };
5524
5525 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5526     
5527     getAutoCreate : function(){
5528         
5529         var cfg = {
5530             tag: 'div',
5531             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5532             cn: [
5533                 {
5534                     tag: 'a',
5535                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5536                 }
5537             ]
5538         };
5539         
5540         return cfg;
5541     }
5542    
5543 });
5544
5545  /*
5546  * Based on:
5547  * Ext JS Library 1.1.1
5548  * Copyright(c) 2006-2007, Ext JS, LLC.
5549  *
5550  * Originally Released Under LGPL - original licence link has changed is not relivant.
5551  *
5552  * Fork - LGPL
5553  * <script type="text/javascript">
5554  */
5555  
5556
5557 /**
5558  * @class Roo.grid.ColumnModel
5559  * @extends Roo.util.Observable
5560  * This is the default implementation of a ColumnModel used by the Grid. It defines
5561  * the columns in the grid.
5562  * <br>Usage:<br>
5563  <pre><code>
5564  var colModel = new Roo.grid.ColumnModel([
5565         {header: "Ticker", width: 60, sortable: true, locked: true},
5566         {header: "Company Name", width: 150, sortable: true},
5567         {header: "Market Cap.", width: 100, sortable: true},
5568         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5569         {header: "Employees", width: 100, sortable: true, resizable: false}
5570  ]);
5571  </code></pre>
5572  * <p>
5573  
5574  * The config options listed for this class are options which may appear in each
5575  * individual column definition.
5576  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5577  * @constructor
5578  * @param {Object} config An Array of column config objects. See this class's
5579  * config objects for details.
5580 */
5581 Roo.grid.ColumnModel = function(config){
5582         /**
5583      * The config passed into the constructor
5584      */
5585     this.config = config;
5586     this.lookup = {};
5587
5588     // if no id, create one
5589     // if the column does not have a dataIndex mapping,
5590     // map it to the order it is in the config
5591     for(var i = 0, len = config.length; i < len; i++){
5592         var c = config[i];
5593         if(typeof c.dataIndex == "undefined"){
5594             c.dataIndex = i;
5595         }
5596         if(typeof c.renderer == "string"){
5597             c.renderer = Roo.util.Format[c.renderer];
5598         }
5599         if(typeof c.id == "undefined"){
5600             c.id = Roo.id();
5601         }
5602         if(c.editor && c.editor.xtype){
5603             c.editor  = Roo.factory(c.editor, Roo.grid);
5604         }
5605         if(c.editor && c.editor.isFormField){
5606             c.editor = new Roo.grid.GridEditor(c.editor);
5607         }
5608         this.lookup[c.id] = c;
5609     }
5610
5611     /**
5612      * The width of columns which have no width specified (defaults to 100)
5613      * @type Number
5614      */
5615     this.defaultWidth = 100;
5616
5617     /**
5618      * Default sortable of columns which have no sortable specified (defaults to false)
5619      * @type Boolean
5620      */
5621     this.defaultSortable = false;
5622
5623     this.addEvents({
5624         /**
5625              * @event widthchange
5626              * Fires when the width of a column changes.
5627              * @param {ColumnModel} this
5628              * @param {Number} columnIndex The column index
5629              * @param {Number} newWidth The new width
5630              */
5631             "widthchange": true,
5632         /**
5633              * @event headerchange
5634              * Fires when the text of a header changes.
5635              * @param {ColumnModel} this
5636              * @param {Number} columnIndex The column index
5637              * @param {Number} newText The new header text
5638              */
5639             "headerchange": true,
5640         /**
5641              * @event hiddenchange
5642              * Fires when a column is hidden or "unhidden".
5643              * @param {ColumnModel} this
5644              * @param {Number} columnIndex The column index
5645              * @param {Boolean} hidden true if hidden, false otherwise
5646              */
5647             "hiddenchange": true,
5648             /**
5649          * @event columnmoved
5650          * Fires when a column is moved.
5651          * @param {ColumnModel} this
5652          * @param {Number} oldIndex
5653          * @param {Number} newIndex
5654          */
5655         "columnmoved" : true,
5656         /**
5657          * @event columlockchange
5658          * Fires when a column's locked state is changed
5659          * @param {ColumnModel} this
5660          * @param {Number} colIndex
5661          * @param {Boolean} locked true if locked
5662          */
5663         "columnlockchange" : true
5664     });
5665     Roo.grid.ColumnModel.superclass.constructor.call(this);
5666 };
5667 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5668     /**
5669      * @cfg {String} header The header text to display in the Grid view.
5670      */
5671     /**
5672      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5673      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5674      * specified, the column's index is used as an index into the Record's data Array.
5675      */
5676     /**
5677      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5678      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5679      */
5680     /**
5681      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5682      * Defaults to the value of the {@link #defaultSortable} property.
5683      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5684      */
5685     /**
5686      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5687      */
5688     /**
5689      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5690      */
5691     /**
5692      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5693      */
5694     /**
5695      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5696      */
5697     /**
5698      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5699      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5700      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5701      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5702      */
5703        /**
5704      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5705      */
5706     /**
5707      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5708      */
5709     /**
5710      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5711      */
5712     /**
5713      * @cfg {String} cursor (Optional)
5714      */
5715     /**
5716      * @cfg {String} tooltip (Optional)
5717      */
5718     /**
5719      * @cfg {Number} xs (Optional)
5720      */
5721     /**
5722      * @cfg {Number} sm (Optional)
5723      */
5724     /**
5725      * @cfg {Number} md (Optional)
5726      */
5727     /**
5728      * @cfg {Number} lg (Optional)
5729      */
5730     /**
5731      * Returns the id of the column at the specified index.
5732      * @param {Number} index The column index
5733      * @return {String} the id
5734      */
5735     getColumnId : function(index){
5736         return this.config[index].id;
5737     },
5738
5739     /**
5740      * Returns the column for a specified id.
5741      * @param {String} id The column id
5742      * @return {Object} the column
5743      */
5744     getColumnById : function(id){
5745         return this.lookup[id];
5746     },
5747
5748     
5749     /**
5750      * Returns the column for a specified dataIndex.
5751      * @param {String} dataIndex The column dataIndex
5752      * @return {Object|Boolean} the column or false if not found
5753      */
5754     getColumnByDataIndex: function(dataIndex){
5755         var index = this.findColumnIndex(dataIndex);
5756         return index > -1 ? this.config[index] : false;
5757     },
5758     
5759     /**
5760      * Returns the index for a specified column id.
5761      * @param {String} id The column id
5762      * @return {Number} the index, or -1 if not found
5763      */
5764     getIndexById : function(id){
5765         for(var i = 0, len = this.config.length; i < len; i++){
5766             if(this.config[i].id == id){
5767                 return i;
5768             }
5769         }
5770         return -1;
5771     },
5772     
5773     /**
5774      * Returns the index for a specified column dataIndex.
5775      * @param {String} dataIndex The column dataIndex
5776      * @return {Number} the index, or -1 if not found
5777      */
5778     
5779     findColumnIndex : function(dataIndex){
5780         for(var i = 0, len = this.config.length; i < len; i++){
5781             if(this.config[i].dataIndex == dataIndex){
5782                 return i;
5783             }
5784         }
5785         return -1;
5786     },
5787     
5788     
5789     moveColumn : function(oldIndex, newIndex){
5790         var c = this.config[oldIndex];
5791         this.config.splice(oldIndex, 1);
5792         this.config.splice(newIndex, 0, c);
5793         this.dataMap = null;
5794         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5795     },
5796
5797     isLocked : function(colIndex){
5798         return this.config[colIndex].locked === true;
5799     },
5800
5801     setLocked : function(colIndex, value, suppressEvent){
5802         if(this.isLocked(colIndex) == value){
5803             return;
5804         }
5805         this.config[colIndex].locked = value;
5806         if(!suppressEvent){
5807             this.fireEvent("columnlockchange", this, colIndex, value);
5808         }
5809     },
5810
5811     getTotalLockedWidth : function(){
5812         var totalWidth = 0;
5813         for(var i = 0; i < this.config.length; i++){
5814             if(this.isLocked(i) && !this.isHidden(i)){
5815                 this.totalWidth += this.getColumnWidth(i);
5816             }
5817         }
5818         return totalWidth;
5819     },
5820
5821     getLockedCount : function(){
5822         for(var i = 0, len = this.config.length; i < len; i++){
5823             if(!this.isLocked(i)){
5824                 return i;
5825             }
5826         }
5827         
5828         return this.config.length;
5829     },
5830
5831     /**
5832      * Returns the number of columns.
5833      * @return {Number}
5834      */
5835     getColumnCount : function(visibleOnly){
5836         if(visibleOnly === true){
5837             var c = 0;
5838             for(var i = 0, len = this.config.length; i < len; i++){
5839                 if(!this.isHidden(i)){
5840                     c++;
5841                 }
5842             }
5843             return c;
5844         }
5845         return this.config.length;
5846     },
5847
5848     /**
5849      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5850      * @param {Function} fn
5851      * @param {Object} scope (optional)
5852      * @return {Array} result
5853      */
5854     getColumnsBy : function(fn, scope){
5855         var r = [];
5856         for(var i = 0, len = this.config.length; i < len; i++){
5857             var c = this.config[i];
5858             if(fn.call(scope||this, c, i) === true){
5859                 r[r.length] = c;
5860             }
5861         }
5862         return r;
5863     },
5864
5865     /**
5866      * Returns true if the specified column is sortable.
5867      * @param {Number} col The column index
5868      * @return {Boolean}
5869      */
5870     isSortable : function(col){
5871         if(typeof this.config[col].sortable == "undefined"){
5872             return this.defaultSortable;
5873         }
5874         return this.config[col].sortable;
5875     },
5876
5877     /**
5878      * Returns the rendering (formatting) function defined for the column.
5879      * @param {Number} col The column index.
5880      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5881      */
5882     getRenderer : function(col){
5883         if(!this.config[col].renderer){
5884             return Roo.grid.ColumnModel.defaultRenderer;
5885         }
5886         return this.config[col].renderer;
5887     },
5888
5889     /**
5890      * Sets the rendering (formatting) function for a column.
5891      * @param {Number} col The column index
5892      * @param {Function} fn The function to use to process the cell's raw data
5893      * to return HTML markup for the grid view. The render function is called with
5894      * the following parameters:<ul>
5895      * <li>Data value.</li>
5896      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5897      * <li>css A CSS style string to apply to the table cell.</li>
5898      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5899      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5900      * <li>Row index</li>
5901      * <li>Column index</li>
5902      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5903      */
5904     setRenderer : function(col, fn){
5905         this.config[col].renderer = fn;
5906     },
5907
5908     /**
5909      * Returns the width for the specified column.
5910      * @param {Number} col The column index
5911      * @return {Number}
5912      */
5913     getColumnWidth : function(col){
5914         return this.config[col].width * 1 || this.defaultWidth;
5915     },
5916
5917     /**
5918      * Sets the width for a column.
5919      * @param {Number} col The column index
5920      * @param {Number} width The new width
5921      */
5922     setColumnWidth : function(col, width, suppressEvent){
5923         this.config[col].width = width;
5924         this.totalWidth = null;
5925         if(!suppressEvent){
5926              this.fireEvent("widthchange", this, col, width);
5927         }
5928     },
5929
5930     /**
5931      * Returns the total width of all columns.
5932      * @param {Boolean} includeHidden True to include hidden column widths
5933      * @return {Number}
5934      */
5935     getTotalWidth : function(includeHidden){
5936         if(!this.totalWidth){
5937             this.totalWidth = 0;
5938             for(var i = 0, len = this.config.length; i < len; i++){
5939                 if(includeHidden || !this.isHidden(i)){
5940                     this.totalWidth += this.getColumnWidth(i);
5941                 }
5942             }
5943         }
5944         return this.totalWidth;
5945     },
5946
5947     /**
5948      * Returns the header for the specified column.
5949      * @param {Number} col The column index
5950      * @return {String}
5951      */
5952     getColumnHeader : function(col){
5953         return this.config[col].header;
5954     },
5955
5956     /**
5957      * Sets the header for a column.
5958      * @param {Number} col The column index
5959      * @param {String} header The new header
5960      */
5961     setColumnHeader : function(col, header){
5962         this.config[col].header = header;
5963         this.fireEvent("headerchange", this, col, header);
5964     },
5965
5966     /**
5967      * Returns the tooltip for the specified column.
5968      * @param {Number} col The column index
5969      * @return {String}
5970      */
5971     getColumnTooltip : function(col){
5972             return this.config[col].tooltip;
5973     },
5974     /**
5975      * Sets the tooltip for a column.
5976      * @param {Number} col The column index
5977      * @param {String} tooltip The new tooltip
5978      */
5979     setColumnTooltip : function(col, tooltip){
5980             this.config[col].tooltip = tooltip;
5981     },
5982
5983     /**
5984      * Returns the dataIndex for the specified column.
5985      * @param {Number} col The column index
5986      * @return {Number}
5987      */
5988     getDataIndex : function(col){
5989         return this.config[col].dataIndex;
5990     },
5991
5992     /**
5993      * Sets the dataIndex for a column.
5994      * @param {Number} col The column index
5995      * @param {Number} dataIndex The new dataIndex
5996      */
5997     setDataIndex : function(col, dataIndex){
5998         this.config[col].dataIndex = dataIndex;
5999     },
6000
6001     
6002     
6003     /**
6004      * Returns true if the cell is editable.
6005      * @param {Number} colIndex The column index
6006      * @param {Number} rowIndex The row index - this is nto actually used..?
6007      * @return {Boolean}
6008      */
6009     isCellEditable : function(colIndex, rowIndex){
6010         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
6011     },
6012
6013     /**
6014      * Returns the editor defined for the cell/column.
6015      * return false or null to disable editing.
6016      * @param {Number} colIndex The column index
6017      * @param {Number} rowIndex The row index
6018      * @return {Object}
6019      */
6020     getCellEditor : function(colIndex, rowIndex){
6021         return this.config[colIndex].editor;
6022     },
6023
6024     /**
6025      * Sets if a column is editable.
6026      * @param {Number} col The column index
6027      * @param {Boolean} editable True if the column is editable
6028      */
6029     setEditable : function(col, editable){
6030         this.config[col].editable = editable;
6031     },
6032
6033
6034     /**
6035      * Returns true if the column is hidden.
6036      * @param {Number} colIndex The column index
6037      * @return {Boolean}
6038      */
6039     isHidden : function(colIndex){
6040         return this.config[colIndex].hidden;
6041     },
6042
6043
6044     /**
6045      * Returns true if the column width cannot be changed
6046      */
6047     isFixed : function(colIndex){
6048         return this.config[colIndex].fixed;
6049     },
6050
6051     /**
6052      * Returns true if the column can be resized
6053      * @return {Boolean}
6054      */
6055     isResizable : function(colIndex){
6056         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6057     },
6058     /**
6059      * Sets if a column is hidden.
6060      * @param {Number} colIndex The column index
6061      * @param {Boolean} hidden True if the column is hidden
6062      */
6063     setHidden : function(colIndex, hidden){
6064         this.config[colIndex].hidden = hidden;
6065         this.totalWidth = null;
6066         this.fireEvent("hiddenchange", this, colIndex, hidden);
6067     },
6068
6069     /**
6070      * Sets the editor for a column.
6071      * @param {Number} col The column index
6072      * @param {Object} editor The editor object
6073      */
6074     setEditor : function(col, editor){
6075         this.config[col].editor = editor;
6076     }
6077 });
6078
6079 Roo.grid.ColumnModel.defaultRenderer = function(value)
6080 {
6081     if(typeof value == "object") {
6082         return value;
6083     }
6084         if(typeof value == "string" && value.length < 1){
6085             return "&#160;";
6086         }
6087     
6088         return String.format("{0}", value);
6089 };
6090
6091 // Alias for backwards compatibility
6092 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6093 /*
6094  * Based on:
6095  * Ext JS Library 1.1.1
6096  * Copyright(c) 2006-2007, Ext JS, LLC.
6097  *
6098  * Originally Released Under LGPL - original licence link has changed is not relivant.
6099  *
6100  * Fork - LGPL
6101  * <script type="text/javascript">
6102  */
6103  
6104 /**
6105  * @class Roo.LoadMask
6106  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6107  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6108  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6109  * element's UpdateManager load indicator and will be destroyed after the initial load.
6110  * @constructor
6111  * Create a new LoadMask
6112  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6113  * @param {Object} config The config object
6114  */
6115 Roo.LoadMask = function(el, config){
6116     this.el = Roo.get(el);
6117     Roo.apply(this, config);
6118     if(this.store){
6119         this.store.on('beforeload', this.onBeforeLoad, this);
6120         this.store.on('load', this.onLoad, this);
6121         this.store.on('loadexception', this.onLoadException, this);
6122         this.removeMask = false;
6123     }else{
6124         var um = this.el.getUpdateManager();
6125         um.showLoadIndicator = false; // disable the default indicator
6126         um.on('beforeupdate', this.onBeforeLoad, this);
6127         um.on('update', this.onLoad, this);
6128         um.on('failure', this.onLoad, this);
6129         this.removeMask = true;
6130     }
6131 };
6132
6133 Roo.LoadMask.prototype = {
6134     /**
6135      * @cfg {Boolean} removeMask
6136      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6137      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6138      */
6139     /**
6140      * @cfg {String} msg
6141      * The text to display in a centered loading message box (defaults to 'Loading...')
6142      */
6143     msg : 'Loading...',
6144     /**
6145      * @cfg {String} msgCls
6146      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6147      */
6148     msgCls : 'x-mask-loading',
6149
6150     /**
6151      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6152      * @type Boolean
6153      */
6154     disabled: false,
6155
6156     /**
6157      * Disables the mask to prevent it from being displayed
6158      */
6159     disable : function(){
6160        this.disabled = true;
6161     },
6162
6163     /**
6164      * Enables the mask so that it can be displayed
6165      */
6166     enable : function(){
6167         this.disabled = false;
6168     },
6169     
6170     onLoadException : function()
6171     {
6172         Roo.log(arguments);
6173         
6174         if (typeof(arguments[3]) != 'undefined') {
6175             Roo.MessageBox.alert("Error loading",arguments[3]);
6176         } 
6177         /*
6178         try {
6179             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6180                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6181             }   
6182         } catch(e) {
6183             
6184         }
6185         */
6186     
6187         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6188     },
6189     // private
6190     onLoad : function()
6191     {
6192         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6193     },
6194
6195     // private
6196     onBeforeLoad : function(){
6197         if(!this.disabled){
6198             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6199         }
6200     },
6201
6202     // private
6203     destroy : function(){
6204         if(this.store){
6205             this.store.un('beforeload', this.onBeforeLoad, this);
6206             this.store.un('load', this.onLoad, this);
6207             this.store.un('loadexception', this.onLoadException, this);
6208         }else{
6209             var um = this.el.getUpdateManager();
6210             um.un('beforeupdate', this.onBeforeLoad, this);
6211             um.un('update', this.onLoad, this);
6212             um.un('failure', this.onLoad, this);
6213         }
6214     }
6215 };/*
6216  * - LGPL
6217  *
6218  * table
6219  * 
6220  */
6221
6222 /**
6223  * @class Roo.bootstrap.Table
6224  * @extends Roo.bootstrap.Component
6225  * Bootstrap Table class
6226  * @cfg {String} cls table class
6227  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6228  * @cfg {String} bgcolor Specifies the background color for a table
6229  * @cfg {Number} border Specifies whether the table cells should have borders or not
6230  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6231  * @cfg {Number} cellspacing Specifies the space between cells
6232  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6233  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6234  * @cfg {String} sortable Specifies that the table should be sortable
6235  * @cfg {String} summary Specifies a summary of the content of a table
6236  * @cfg {Number} width Specifies the width of a table
6237  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6238  * 
6239  * @cfg {boolean} striped Should the rows be alternative striped
6240  * @cfg {boolean} bordered Add borders to the table
6241  * @cfg {boolean} hover Add hover highlighting
6242  * @cfg {boolean} condensed Format condensed
6243  * @cfg {boolean} responsive Format condensed
6244  * @cfg {Boolean} loadMask (true|false) default false
6245  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6246  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6247  * @cfg {Boolean} rowSelection (true|false) default false
6248  * @cfg {Boolean} cellSelection (true|false) default false
6249  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6250  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6251  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6252  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6253  
6254  * 
6255  * @constructor
6256  * Create a new Table
6257  * @param {Object} config The config object
6258  */
6259
6260 Roo.bootstrap.Table = function(config){
6261     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6262     
6263   
6264     
6265     // BC...
6266     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6267     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6268     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6269     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6270     
6271     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6272     if (this.sm) {
6273         this.sm.grid = this;
6274         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6275         this.sm = this.selModel;
6276         this.sm.xmodule = this.xmodule || false;
6277     }
6278     
6279     if (this.cm && typeof(this.cm.config) == 'undefined') {
6280         this.colModel = new Roo.grid.ColumnModel(this.cm);
6281         this.cm = this.colModel;
6282         this.cm.xmodule = this.xmodule || false;
6283     }
6284     if (this.store) {
6285         this.store= Roo.factory(this.store, Roo.data);
6286         this.ds = this.store;
6287         this.ds.xmodule = this.xmodule || false;
6288          
6289     }
6290     if (this.footer && this.store) {
6291         this.footer.dataSource = this.ds;
6292         this.footer = Roo.factory(this.footer);
6293     }
6294     
6295     /** @private */
6296     this.addEvents({
6297         /**
6298          * @event cellclick
6299          * Fires when a cell is clicked
6300          * @param {Roo.bootstrap.Table} this
6301          * @param {Roo.Element} el
6302          * @param {Number} rowIndex
6303          * @param {Number} columnIndex
6304          * @param {Roo.EventObject} e
6305          */
6306         "cellclick" : true,
6307         /**
6308          * @event celldblclick
6309          * Fires when a cell is double clicked
6310          * @param {Roo.bootstrap.Table} this
6311          * @param {Roo.Element} el
6312          * @param {Number} rowIndex
6313          * @param {Number} columnIndex
6314          * @param {Roo.EventObject} e
6315          */
6316         "celldblclick" : true,
6317         /**
6318          * @event rowclick
6319          * Fires when a row is clicked
6320          * @param {Roo.bootstrap.Table} this
6321          * @param {Roo.Element} el
6322          * @param {Number} rowIndex
6323          * @param {Roo.EventObject} e
6324          */
6325         "rowclick" : true,
6326         /**
6327          * @event rowdblclick
6328          * Fires when a row is double clicked
6329          * @param {Roo.bootstrap.Table} this
6330          * @param {Roo.Element} el
6331          * @param {Number} rowIndex
6332          * @param {Roo.EventObject} e
6333          */
6334         "rowdblclick" : true,
6335         /**
6336          * @event mouseover
6337          * Fires when a mouseover occur
6338          * @param {Roo.bootstrap.Table} this
6339          * @param {Roo.Element} el
6340          * @param {Number} rowIndex
6341          * @param {Number} columnIndex
6342          * @param {Roo.EventObject} e
6343          */
6344         "mouseover" : true,
6345         /**
6346          * @event mouseout
6347          * Fires when a mouseout occur
6348          * @param {Roo.bootstrap.Table} this
6349          * @param {Roo.Element} el
6350          * @param {Number} rowIndex
6351          * @param {Number} columnIndex
6352          * @param {Roo.EventObject} e
6353          */
6354         "mouseout" : true,
6355         /**
6356          * @event rowclass
6357          * Fires when a row is rendered, so you can change add a style to it.
6358          * @param {Roo.bootstrap.Table} this
6359          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6360          */
6361         'rowclass' : true,
6362           /**
6363          * @event rowsrendered
6364          * Fires when all the  rows have been rendered
6365          * @param {Roo.bootstrap.Table} this
6366          */
6367         'rowsrendered' : true,
6368         /**
6369          * @event contextmenu
6370          * The raw contextmenu event for the entire grid.
6371          * @param {Roo.EventObject} e
6372          */
6373         "contextmenu" : true,
6374         /**
6375          * @event rowcontextmenu
6376          * Fires when a row is right clicked
6377          * @param {Roo.bootstrap.Table} this
6378          * @param {Number} rowIndex
6379          * @param {Roo.EventObject} e
6380          */
6381         "rowcontextmenu" : true,
6382         /**
6383          * @event cellcontextmenu
6384          * Fires when a cell is right clicked
6385          * @param {Roo.bootstrap.Table} this
6386          * @param {Number} rowIndex
6387          * @param {Number} cellIndex
6388          * @param {Roo.EventObject} e
6389          */
6390          "cellcontextmenu" : true,
6391          /**
6392          * @event headercontextmenu
6393          * Fires when a header is right clicked
6394          * @param {Roo.bootstrap.Table} this
6395          * @param {Number} columnIndex
6396          * @param {Roo.EventObject} e
6397          */
6398         "headercontextmenu" : true
6399     });
6400 };
6401
6402 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6403     
6404     cls: false,
6405     align: false,
6406     bgcolor: false,
6407     border: false,
6408     cellpadding: false,
6409     cellspacing: false,
6410     frame: false,
6411     rules: false,
6412     sortable: false,
6413     summary: false,
6414     width: false,
6415     striped : false,
6416     scrollBody : false,
6417     bordered: false,
6418     hover:  false,
6419     condensed : false,
6420     responsive : false,
6421     sm : false,
6422     cm : false,
6423     store : false,
6424     loadMask : false,
6425     footerShow : true,
6426     headerShow : true,
6427   
6428     rowSelection : false,
6429     cellSelection : false,
6430     layout : false,
6431     
6432     // Roo.Element - the tbody
6433     mainBody: false,
6434     // Roo.Element - thead element
6435     mainHead: false,
6436     
6437     container: false, // used by gridpanel...
6438     
6439     lazyLoad : false,
6440     
6441     CSS : Roo.util.CSS,
6442     
6443     auto_hide_footer : false,
6444     
6445     getAutoCreate : function()
6446     {
6447         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6448         
6449         cfg = {
6450             tag: 'table',
6451             cls : 'table',
6452             cn : []
6453         };
6454         if (this.scrollBody) {
6455             cfg.cls += ' table-body-fixed';
6456         }    
6457         if (this.striped) {
6458             cfg.cls += ' table-striped';
6459         }
6460         
6461         if (this.hover) {
6462             cfg.cls += ' table-hover';
6463         }
6464         if (this.bordered) {
6465             cfg.cls += ' table-bordered';
6466         }
6467         if (this.condensed) {
6468             cfg.cls += ' table-condensed';
6469         }
6470         if (this.responsive) {
6471             cfg.cls += ' table-responsive';
6472         }
6473         
6474         if (this.cls) {
6475             cfg.cls+=  ' ' +this.cls;
6476         }
6477         
6478         // this lot should be simplifed...
6479         var _t = this;
6480         var cp = [
6481             'align',
6482             'bgcolor',
6483             'border',
6484             'cellpadding',
6485             'cellspacing',
6486             'frame',
6487             'rules',
6488             'sortable',
6489             'summary',
6490             'width'
6491         ].forEach(function(k) {
6492             if (_t[k]) {
6493                 cfg[k] = _t[k];
6494             }
6495         });
6496         
6497         
6498         if (this.layout) {
6499             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6500         }
6501         
6502         if(this.store || this.cm){
6503             if(this.headerShow){
6504                 cfg.cn.push(this.renderHeader());
6505             }
6506             
6507             cfg.cn.push(this.renderBody());
6508             
6509             if(this.footerShow){
6510                 cfg.cn.push(this.renderFooter());
6511             }
6512             // where does this come from?
6513             //cfg.cls+=  ' TableGrid';
6514         }
6515         
6516         return { cn : [ cfg ] };
6517     },
6518     
6519     initEvents : function()
6520     {   
6521         if(!this.store || !this.cm){
6522             return;
6523         }
6524         if (this.selModel) {
6525             this.selModel.initEvents();
6526         }
6527         
6528         
6529         //Roo.log('initEvents with ds!!!!');
6530         
6531         this.mainBody = this.el.select('tbody', true).first();
6532         this.mainHead = this.el.select('thead', true).first();
6533         this.mainFoot = this.el.select('tfoot', true).first();
6534         
6535         
6536         
6537         var _this = this;
6538         
6539         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6540             e.on('click', _this.sort, _this);
6541         });
6542         
6543         this.mainBody.on("click", this.onClick, this);
6544         this.mainBody.on("dblclick", this.onDblClick, this);
6545         
6546         // why is this done????? = it breaks dialogs??
6547         //this.parent().el.setStyle('position', 'relative');
6548         
6549         
6550         if (this.footer) {
6551             this.footer.parentId = this.id;
6552             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6553             
6554             if(this.lazyLoad){
6555                 this.el.select('tfoot tr td').first().addClass('hide');
6556             }
6557         } 
6558         
6559         if(this.loadMask) {
6560             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6561         }
6562         
6563         this.store.on('load', this.onLoad, this);
6564         this.store.on('beforeload', this.onBeforeLoad, this);
6565         this.store.on('update', this.onUpdate, this);
6566         this.store.on('add', this.onAdd, this);
6567         this.store.on("clear", this.clear, this);
6568         
6569         this.el.on("contextmenu", this.onContextMenu, this);
6570         
6571         this.mainBody.on('scroll', this.onBodyScroll, this);
6572         
6573         this.cm.on("headerchange", this.onHeaderChange, this);
6574         
6575         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6576         
6577     },
6578     
6579     onContextMenu : function(e, t)
6580     {
6581         this.processEvent("contextmenu", e);
6582     },
6583     
6584     processEvent : function(name, e)
6585     {
6586         if (name != 'touchstart' ) {
6587             this.fireEvent(name, e);    
6588         }
6589         
6590         var t = e.getTarget();
6591         
6592         var cell = Roo.get(t);
6593         
6594         if(!cell){
6595             return;
6596         }
6597         
6598         if(cell.findParent('tfoot', false, true)){
6599             return;
6600         }
6601         
6602         if(cell.findParent('thead', false, true)){
6603             
6604             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6605                 cell = Roo.get(t).findParent('th', false, true);
6606                 if (!cell) {
6607                     Roo.log("failed to find th in thead?");
6608                     Roo.log(e.getTarget());
6609                     return;
6610                 }
6611             }
6612             
6613             var cellIndex = cell.dom.cellIndex;
6614             
6615             var ename = name == 'touchstart' ? 'click' : name;
6616             this.fireEvent("header" + ename, this, cellIndex, e);
6617             
6618             return;
6619         }
6620         
6621         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6622             cell = Roo.get(t).findParent('td', false, true);
6623             if (!cell) {
6624                 Roo.log("failed to find th in tbody?");
6625                 Roo.log(e.getTarget());
6626                 return;
6627             }
6628         }
6629         
6630         var row = cell.findParent('tr', false, true);
6631         var cellIndex = cell.dom.cellIndex;
6632         var rowIndex = row.dom.rowIndex - 1;
6633         
6634         if(row !== false){
6635             
6636             this.fireEvent("row" + name, this, rowIndex, e);
6637             
6638             if(cell !== false){
6639             
6640                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6641             }
6642         }
6643         
6644     },
6645     
6646     onMouseover : function(e, el)
6647     {
6648         var cell = Roo.get(el);
6649         
6650         if(!cell){
6651             return;
6652         }
6653         
6654         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6655             cell = cell.findParent('td', false, true);
6656         }
6657         
6658         var row = cell.findParent('tr', false, true);
6659         var cellIndex = cell.dom.cellIndex;
6660         var rowIndex = row.dom.rowIndex - 1; // start from 0
6661         
6662         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6663         
6664     },
6665     
6666     onMouseout : function(e, el)
6667     {
6668         var cell = Roo.get(el);
6669         
6670         if(!cell){
6671             return;
6672         }
6673         
6674         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6675             cell = cell.findParent('td', false, true);
6676         }
6677         
6678         var row = cell.findParent('tr', false, true);
6679         var cellIndex = cell.dom.cellIndex;
6680         var rowIndex = row.dom.rowIndex - 1; // start from 0
6681         
6682         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6683         
6684     },
6685     
6686     onClick : function(e, el)
6687     {
6688         var cell = Roo.get(el);
6689         
6690         if(!cell || (!this.cellSelection && !this.rowSelection)){
6691             return;
6692         }
6693         
6694         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6695             cell = cell.findParent('td', false, true);
6696         }
6697         
6698         if(!cell || typeof(cell) == 'undefined'){
6699             return;
6700         }
6701         
6702         var row = cell.findParent('tr', false, true);
6703         
6704         if(!row || typeof(row) == 'undefined'){
6705             return;
6706         }
6707         
6708         var cellIndex = cell.dom.cellIndex;
6709         var rowIndex = this.getRowIndex(row);
6710         
6711         // why??? - should these not be based on SelectionModel?
6712         if(this.cellSelection){
6713             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6714         }
6715         
6716         if(this.rowSelection){
6717             this.fireEvent('rowclick', this, row, rowIndex, e);
6718         }
6719         
6720         
6721     },
6722         
6723     onDblClick : function(e,el)
6724     {
6725         var cell = Roo.get(el);
6726         
6727         if(!cell || (!this.cellSelection && !this.rowSelection)){
6728             return;
6729         }
6730         
6731         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6732             cell = cell.findParent('td', false, true);
6733         }
6734         
6735         if(!cell || typeof(cell) == 'undefined'){
6736             return;
6737         }
6738         
6739         var row = cell.findParent('tr', false, true);
6740         
6741         if(!row || typeof(row) == 'undefined'){
6742             return;
6743         }
6744         
6745         var cellIndex = cell.dom.cellIndex;
6746         var rowIndex = this.getRowIndex(row);
6747         
6748         if(this.cellSelection){
6749             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6750         }
6751         
6752         if(this.rowSelection){
6753             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6754         }
6755     },
6756     
6757     sort : function(e,el)
6758     {
6759         var col = Roo.get(el);
6760         
6761         if(!col.hasClass('sortable')){
6762             return;
6763         }
6764         
6765         var sort = col.attr('sort');
6766         var dir = 'ASC';
6767         
6768         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6769             dir = 'DESC';
6770         }
6771         
6772         this.store.sortInfo = {field : sort, direction : dir};
6773         
6774         if (this.footer) {
6775             Roo.log("calling footer first");
6776             this.footer.onClick('first');
6777         } else {
6778         
6779             this.store.load({ params : { start : 0 } });
6780         }
6781     },
6782     
6783     renderHeader : function()
6784     {
6785         var header = {
6786             tag: 'thead',
6787             cn : []
6788         };
6789         
6790         var cm = this.cm;
6791         this.totalWidth = 0;
6792         
6793         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6794             
6795             var config = cm.config[i];
6796             
6797             var c = {
6798                 tag: 'th',
6799                 cls : 'x-hcol-' + i,
6800                 style : '',
6801                 html: cm.getColumnHeader(i)
6802             };
6803             
6804             var hh = '';
6805             
6806             if(typeof(config.sortable) != 'undefined' && config.sortable){
6807                 c.cls = 'sortable';
6808                 c.html = '<i class="glyphicon"></i>' + c.html;
6809             }
6810             
6811             if(typeof(config.lgHeader) != 'undefined'){
6812                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6813             }
6814             
6815             if(typeof(config.mdHeader) != 'undefined'){
6816                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6817             }
6818             
6819             if(typeof(config.smHeader) != 'undefined'){
6820                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6821             }
6822             
6823             if(typeof(config.xsHeader) != 'undefined'){
6824                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6825             }
6826             
6827             if(hh.length){
6828                 c.html = hh;
6829             }
6830             
6831             if(typeof(config.tooltip) != 'undefined'){
6832                 c.tooltip = config.tooltip;
6833             }
6834             
6835             if(typeof(config.colspan) != 'undefined'){
6836                 c.colspan = config.colspan;
6837             }
6838             
6839             if(typeof(config.hidden) != 'undefined' && config.hidden){
6840                 c.style += ' display:none;';
6841             }
6842             
6843             if(typeof(config.dataIndex) != 'undefined'){
6844                 c.sort = config.dataIndex;
6845             }
6846             
6847            
6848             
6849             if(typeof(config.align) != 'undefined' && config.align.length){
6850                 c.style += ' text-align:' + config.align + ';';
6851             }
6852             
6853             if(typeof(config.width) != 'undefined'){
6854                 c.style += ' width:' + config.width + 'px;';
6855                 this.totalWidth += config.width;
6856             } else {
6857                 this.totalWidth += 100; // assume minimum of 100 per column?
6858             }
6859             
6860             if(typeof(config.cls) != 'undefined'){
6861                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6862             }
6863             
6864             ['xs','sm','md','lg'].map(function(size){
6865                 
6866                 if(typeof(config[size]) == 'undefined'){
6867                     return;
6868                 }
6869                 
6870                 if (!config[size]) { // 0 = hidden
6871                     c.cls += ' hidden-' + size;
6872                     return;
6873                 }
6874                 
6875                 c.cls += ' col-' + size + '-' + config[size];
6876
6877             });
6878             
6879             header.cn.push(c)
6880         }
6881         
6882         return header;
6883     },
6884     
6885     renderBody : function()
6886     {
6887         var body = {
6888             tag: 'tbody',
6889             cn : [
6890                 {
6891                     tag: 'tr',
6892                     cn : [
6893                         {
6894                             tag : 'td',
6895                             colspan :  this.cm.getColumnCount()
6896                         }
6897                     ]
6898                 }
6899             ]
6900         };
6901         
6902         return body;
6903     },
6904     
6905     renderFooter : function()
6906     {
6907         var footer = {
6908             tag: 'tfoot',
6909             cn : [
6910                 {
6911                     tag: 'tr',
6912                     cn : [
6913                         {
6914                             tag : 'td',
6915                             colspan :  this.cm.getColumnCount()
6916                         }
6917                     ]
6918                 }
6919             ]
6920         };
6921         
6922         return footer;
6923     },
6924     
6925     
6926     
6927     onLoad : function()
6928     {
6929 //        Roo.log('ds onload');
6930         this.clear();
6931         
6932         var _this = this;
6933         var cm = this.cm;
6934         var ds = this.store;
6935         
6936         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6937             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6938             if (_this.store.sortInfo) {
6939                     
6940                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6941                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6942                 }
6943                 
6944                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6945                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6946                 }
6947             }
6948         });
6949         
6950         var tbody =  this.mainBody;
6951               
6952         if(ds.getCount() > 0){
6953             ds.data.each(function(d,rowIndex){
6954                 var row =  this.renderRow(cm, ds, rowIndex);
6955                 
6956                 tbody.createChild(row);
6957                 
6958                 var _this = this;
6959                 
6960                 if(row.cellObjects.length){
6961                     Roo.each(row.cellObjects, function(r){
6962                         _this.renderCellObject(r);
6963                     })
6964                 }
6965                 
6966             }, this);
6967         }
6968         
6969         var tfoot = this.el.select('tfoot', true).first();
6970         
6971         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6972             
6973             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6974             
6975             var total = this.ds.getTotalCount();
6976             
6977             if(this.footer.pageSize < total){
6978                 this.mainFoot.show();
6979             }
6980         }
6981         
6982         Roo.each(this.el.select('tbody td', true).elements, function(e){
6983             e.on('mouseover', _this.onMouseover, _this);
6984         });
6985         
6986         Roo.each(this.el.select('tbody td', true).elements, function(e){
6987             e.on('mouseout', _this.onMouseout, _this);
6988         });
6989         this.fireEvent('rowsrendered', this);
6990         
6991         this.autoSize();
6992     },
6993     
6994     
6995     onUpdate : function(ds,record)
6996     {
6997         this.refreshRow(record);
6998         this.autoSize();
6999     },
7000     
7001     onRemove : function(ds, record, index, isUpdate){
7002         if(isUpdate !== true){
7003             this.fireEvent("beforerowremoved", this, index, record);
7004         }
7005         var bt = this.mainBody.dom;
7006         
7007         var rows = this.el.select('tbody > tr', true).elements;
7008         
7009         if(typeof(rows[index]) != 'undefined'){
7010             bt.removeChild(rows[index].dom);
7011         }
7012         
7013 //        if(bt.rows[index]){
7014 //            bt.removeChild(bt.rows[index]);
7015 //        }
7016         
7017         if(isUpdate !== true){
7018             //this.stripeRows(index);
7019             //this.syncRowHeights(index, index);
7020             //this.layout();
7021             this.fireEvent("rowremoved", this, index, record);
7022         }
7023     },
7024     
7025     onAdd : function(ds, records, rowIndex)
7026     {
7027         //Roo.log('on Add called');
7028         // - note this does not handle multiple adding very well..
7029         var bt = this.mainBody.dom;
7030         for (var i =0 ; i < records.length;i++) {
7031             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
7032             //Roo.log(records[i]);
7033             //Roo.log(this.store.getAt(rowIndex+i));
7034             this.insertRow(this.store, rowIndex + i, false);
7035             return;
7036         }
7037         
7038     },
7039     
7040     
7041     refreshRow : function(record){
7042         var ds = this.store, index;
7043         if(typeof record == 'number'){
7044             index = record;
7045             record = ds.getAt(index);
7046         }else{
7047             index = ds.indexOf(record);
7048         }
7049         this.insertRow(ds, index, true);
7050         this.autoSize();
7051         this.onRemove(ds, record, index+1, true);
7052         this.autoSize();
7053         //this.syncRowHeights(index, index);
7054         //this.layout();
7055         this.fireEvent("rowupdated", this, index, record);
7056     },
7057     
7058     insertRow : function(dm, rowIndex, isUpdate){
7059         
7060         if(!isUpdate){
7061             this.fireEvent("beforerowsinserted", this, rowIndex);
7062         }
7063             //var s = this.getScrollState();
7064         var row = this.renderRow(this.cm, this.store, rowIndex);
7065         // insert before rowIndex..
7066         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7067         
7068         var _this = this;
7069                 
7070         if(row.cellObjects.length){
7071             Roo.each(row.cellObjects, function(r){
7072                 _this.renderCellObject(r);
7073             })
7074         }
7075             
7076         if(!isUpdate){
7077             this.fireEvent("rowsinserted", this, rowIndex);
7078             //this.syncRowHeights(firstRow, lastRow);
7079             //this.stripeRows(firstRow);
7080             //this.layout();
7081         }
7082         
7083     },
7084     
7085     
7086     getRowDom : function(rowIndex)
7087     {
7088         var rows = this.el.select('tbody > tr', true).elements;
7089         
7090         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7091         
7092     },
7093     // returns the object tree for a tr..
7094   
7095     
7096     renderRow : function(cm, ds, rowIndex) 
7097     {
7098         var d = ds.getAt(rowIndex);
7099         
7100         var row = {
7101             tag : 'tr',
7102             cls : 'x-row-' + rowIndex,
7103             cn : []
7104         };
7105             
7106         var cellObjects = [];
7107         
7108         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7109             var config = cm.config[i];
7110             
7111             var renderer = cm.getRenderer(i);
7112             var value = '';
7113             var id = false;
7114             
7115             if(typeof(renderer) !== 'undefined'){
7116                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7117             }
7118             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7119             // and are rendered into the cells after the row is rendered - using the id for the element.
7120             
7121             if(typeof(value) === 'object'){
7122                 id = Roo.id();
7123                 cellObjects.push({
7124                     container : id,
7125                     cfg : value 
7126                 })
7127             }
7128             
7129             var rowcfg = {
7130                 record: d,
7131                 rowIndex : rowIndex,
7132                 colIndex : i,
7133                 rowClass : ''
7134             };
7135
7136             this.fireEvent('rowclass', this, rowcfg);
7137             
7138             var td = {
7139                 tag: 'td',
7140                 cls : rowcfg.rowClass + ' x-col-' + i,
7141                 style: '',
7142                 html: (typeof(value) === 'object') ? '' : value
7143             };
7144             
7145             if (id) {
7146                 td.id = id;
7147             }
7148             
7149             if(typeof(config.colspan) != 'undefined'){
7150                 td.colspan = config.colspan;
7151             }
7152             
7153             if(typeof(config.hidden) != 'undefined' && config.hidden){
7154                 td.style += ' display:none;';
7155             }
7156             
7157             if(typeof(config.align) != 'undefined' && config.align.length){
7158                 td.style += ' text-align:' + config.align + ';';
7159             }
7160             if(typeof(config.valign) != 'undefined' && config.valign.length){
7161                 td.style += ' vertical-align:' + config.valign + ';';
7162             }
7163             
7164             if(typeof(config.width) != 'undefined'){
7165                 td.style += ' width:' +  config.width + 'px;';
7166             }
7167             
7168             if(typeof(config.cursor) != 'undefined'){
7169                 td.style += ' cursor:' +  config.cursor + ';';
7170             }
7171             
7172             if(typeof(config.cls) != 'undefined'){
7173                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7174             }
7175             
7176             ['xs','sm','md','lg'].map(function(size){
7177                 
7178                 if(typeof(config[size]) == 'undefined'){
7179                     return;
7180                 }
7181                 
7182                 if (!config[size]) { // 0 = hidden
7183                     td.cls += ' hidden-' + size;
7184                     return;
7185                 }
7186                 
7187                 td.cls += ' col-' + size + '-' + config[size];
7188
7189             });
7190             
7191             row.cn.push(td);
7192            
7193         }
7194         
7195         row.cellObjects = cellObjects;
7196         
7197         return row;
7198           
7199     },
7200     
7201     
7202     
7203     onBeforeLoad : function()
7204     {
7205         
7206     },
7207      /**
7208      * Remove all rows
7209      */
7210     clear : function()
7211     {
7212         this.el.select('tbody', true).first().dom.innerHTML = '';
7213     },
7214     /**
7215      * Show or hide a row.
7216      * @param {Number} rowIndex to show or hide
7217      * @param {Boolean} state hide
7218      */
7219     setRowVisibility : function(rowIndex, state)
7220     {
7221         var bt = this.mainBody.dom;
7222         
7223         var rows = this.el.select('tbody > tr', true).elements;
7224         
7225         if(typeof(rows[rowIndex]) == 'undefined'){
7226             return;
7227         }
7228         rows[rowIndex].dom.style.display = state ? '' : 'none';
7229     },
7230     
7231     
7232     getSelectionModel : function(){
7233         if(!this.selModel){
7234             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7235         }
7236         return this.selModel;
7237     },
7238     /*
7239      * Render the Roo.bootstrap object from renderder
7240      */
7241     renderCellObject : function(r)
7242     {
7243         var _this = this;
7244         
7245         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7246         
7247         var t = r.cfg.render(r.container);
7248         
7249         if(r.cfg.cn){
7250             Roo.each(r.cfg.cn, function(c){
7251                 var child = {
7252                     container: t.getChildContainer(),
7253                     cfg: c
7254                 };
7255                 _this.renderCellObject(child);
7256             })
7257         }
7258     },
7259     
7260     getRowIndex : function(row)
7261     {
7262         var rowIndex = -1;
7263         
7264         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7265             if(el != row){
7266                 return;
7267             }
7268             
7269             rowIndex = index;
7270         });
7271         
7272         return rowIndex;
7273     },
7274      /**
7275      * Returns the grid's underlying element = used by panel.Grid
7276      * @return {Element} The element
7277      */
7278     getGridEl : function(){
7279         return this.el;
7280     },
7281      /**
7282      * Forces a resize - used by panel.Grid
7283      * @return {Element} The element
7284      */
7285     autoSize : function()
7286     {
7287         //var ctr = Roo.get(this.container.dom.parentElement);
7288         var ctr = Roo.get(this.el.dom);
7289         
7290         var thd = this.getGridEl().select('thead',true).first();
7291         var tbd = this.getGridEl().select('tbody', true).first();
7292         var tfd = this.getGridEl().select('tfoot', true).first();
7293         
7294         var cw = ctr.getWidth();
7295         
7296         if (tbd) {
7297             
7298             tbd.setSize(ctr.getWidth(),
7299                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7300             );
7301             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7302             cw -= barsize;
7303         }
7304         cw = Math.max(cw, this.totalWidth);
7305         this.getGridEl().select('tr',true).setWidth(cw);
7306         // resize 'expandable coloumn?
7307         
7308         return; // we doe not have a view in this design..
7309         
7310     },
7311     onBodyScroll: function()
7312     {
7313         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7314         if(this.mainHead){
7315             this.mainHead.setStyle({
7316                 'position' : 'relative',
7317                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7318             });
7319         }
7320         
7321         if(this.lazyLoad){
7322             
7323             var scrollHeight = this.mainBody.dom.scrollHeight;
7324             
7325             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7326             
7327             var height = this.mainBody.getHeight();
7328             
7329             if(scrollHeight - height == scrollTop) {
7330                 
7331                 var total = this.ds.getTotalCount();
7332                 
7333                 if(this.footer.cursor + this.footer.pageSize < total){
7334                     
7335                     this.footer.ds.load({
7336                         params : {
7337                             start : this.footer.cursor + this.footer.pageSize,
7338                             limit : this.footer.pageSize
7339                         },
7340                         add : true
7341                     });
7342                 }
7343             }
7344             
7345         }
7346     },
7347     
7348     onHeaderChange : function()
7349     {
7350         var header = this.renderHeader();
7351         var table = this.el.select('table', true).first();
7352         
7353         this.mainHead.remove();
7354         this.mainHead = table.createChild(header, this.mainBody, false);
7355     },
7356     
7357     onHiddenChange : function(colModel, colIndex, hidden)
7358     {
7359         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7360         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7361         
7362         this.CSS.updateRule(thSelector, "display", "");
7363         this.CSS.updateRule(tdSelector, "display", "");
7364         
7365         if(hidden){
7366             this.CSS.updateRule(thSelector, "display", "none");
7367             this.CSS.updateRule(tdSelector, "display", "none");
7368         }
7369         
7370         this.onHeaderChange();
7371         this.onLoad();
7372     },
7373     
7374     setColumnWidth: function(col_index, width)
7375     {
7376         // width = "md-2 xs-2..."
7377         if(!this.colModel.config[col_index]) {
7378             return;
7379         }
7380         
7381         var w = width.split(" ");
7382         
7383         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7384         
7385         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7386         
7387         
7388         for(var j = 0; j < w.length; j++) {
7389             
7390             if(!w[j]) {
7391                 continue;
7392             }
7393             
7394             var size_cls = w[j].split("-");
7395             
7396             if(!Number.isInteger(size_cls[1] * 1)) {
7397                 continue;
7398             }
7399             
7400             if(!this.colModel.config[col_index][size_cls[0]]) {
7401                 continue;
7402             }
7403             
7404             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7405                 continue;
7406             }
7407             
7408             h_row[0].classList.replace(
7409                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7410                 "col-"+size_cls[0]+"-"+size_cls[1]
7411             );
7412             
7413             for(var i = 0; i < rows.length; i++) {
7414                 
7415                 var size_cls = w[j].split("-");
7416                 
7417                 if(!Number.isInteger(size_cls[1] * 1)) {
7418                     continue;
7419                 }
7420                 
7421                 if(!this.colModel.config[col_index][size_cls[0]]) {
7422                     continue;
7423                 }
7424                 
7425                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7426                     continue;
7427                 }
7428                 
7429                 rows[i].classList.replace(
7430                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7431                     "col-"+size_cls[0]+"-"+size_cls[1]
7432                 );
7433             }
7434             
7435             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7436         }
7437     }
7438 });
7439
7440  
7441
7442  /*
7443  * - LGPL
7444  *
7445  * table cell
7446  * 
7447  */
7448
7449 /**
7450  * @class Roo.bootstrap.TableCell
7451  * @extends Roo.bootstrap.Component
7452  * Bootstrap TableCell class
7453  * @cfg {String} html cell contain text
7454  * @cfg {String} cls cell class
7455  * @cfg {String} tag cell tag (td|th) default td
7456  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7457  * @cfg {String} align Aligns the content in a cell
7458  * @cfg {String} axis Categorizes cells
7459  * @cfg {String} bgcolor Specifies the background color of a cell
7460  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7461  * @cfg {Number} colspan Specifies the number of columns a cell should span
7462  * @cfg {String} headers Specifies one or more header cells a cell is related to
7463  * @cfg {Number} height Sets the height of a cell
7464  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7465  * @cfg {Number} rowspan Sets the number of rows a cell should span
7466  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7467  * @cfg {String} valign Vertical aligns the content in a cell
7468  * @cfg {Number} width Specifies the width of a cell
7469  * 
7470  * @constructor
7471  * Create a new TableCell
7472  * @param {Object} config The config object
7473  */
7474
7475 Roo.bootstrap.TableCell = function(config){
7476     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7477 };
7478
7479 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7480     
7481     html: false,
7482     cls: false,
7483     tag: false,
7484     abbr: false,
7485     align: false,
7486     axis: false,
7487     bgcolor: false,
7488     charoff: false,
7489     colspan: false,
7490     headers: false,
7491     height: false,
7492     nowrap: false,
7493     rowspan: false,
7494     scope: false,
7495     valign: false,
7496     width: false,
7497     
7498     
7499     getAutoCreate : function(){
7500         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7501         
7502         cfg = {
7503             tag: 'td'
7504         };
7505         
7506         if(this.tag){
7507             cfg.tag = this.tag;
7508         }
7509         
7510         if (this.html) {
7511             cfg.html=this.html
7512         }
7513         if (this.cls) {
7514             cfg.cls=this.cls
7515         }
7516         if (this.abbr) {
7517             cfg.abbr=this.abbr
7518         }
7519         if (this.align) {
7520             cfg.align=this.align
7521         }
7522         if (this.axis) {
7523             cfg.axis=this.axis
7524         }
7525         if (this.bgcolor) {
7526             cfg.bgcolor=this.bgcolor
7527         }
7528         if (this.charoff) {
7529             cfg.charoff=this.charoff
7530         }
7531         if (this.colspan) {
7532             cfg.colspan=this.colspan
7533         }
7534         if (this.headers) {
7535             cfg.headers=this.headers
7536         }
7537         if (this.height) {
7538             cfg.height=this.height
7539         }
7540         if (this.nowrap) {
7541             cfg.nowrap=this.nowrap
7542         }
7543         if (this.rowspan) {
7544             cfg.rowspan=this.rowspan
7545         }
7546         if (this.scope) {
7547             cfg.scope=this.scope
7548         }
7549         if (this.valign) {
7550             cfg.valign=this.valign
7551         }
7552         if (this.width) {
7553             cfg.width=this.width
7554         }
7555         
7556         
7557         return cfg;
7558     }
7559    
7560 });
7561
7562  
7563
7564  /*
7565  * - LGPL
7566  *
7567  * table row
7568  * 
7569  */
7570
7571 /**
7572  * @class Roo.bootstrap.TableRow
7573  * @extends Roo.bootstrap.Component
7574  * Bootstrap TableRow class
7575  * @cfg {String} cls row class
7576  * @cfg {String} align Aligns the content in a table row
7577  * @cfg {String} bgcolor Specifies a background color for a table row
7578  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7579  * @cfg {String} valign Vertical aligns the content in a table row
7580  * 
7581  * @constructor
7582  * Create a new TableRow
7583  * @param {Object} config The config object
7584  */
7585
7586 Roo.bootstrap.TableRow = function(config){
7587     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7588 };
7589
7590 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7591     
7592     cls: false,
7593     align: false,
7594     bgcolor: false,
7595     charoff: false,
7596     valign: false,
7597     
7598     getAutoCreate : function(){
7599         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7600         
7601         cfg = {
7602             tag: 'tr'
7603         };
7604             
7605         if(this.cls){
7606             cfg.cls = this.cls;
7607         }
7608         if(this.align){
7609             cfg.align = this.align;
7610         }
7611         if(this.bgcolor){
7612             cfg.bgcolor = this.bgcolor;
7613         }
7614         if(this.charoff){
7615             cfg.charoff = this.charoff;
7616         }
7617         if(this.valign){
7618             cfg.valign = this.valign;
7619         }
7620         
7621         return cfg;
7622     }
7623    
7624 });
7625
7626  
7627
7628  /*
7629  * - LGPL
7630  *
7631  * table body
7632  * 
7633  */
7634
7635 /**
7636  * @class Roo.bootstrap.TableBody
7637  * @extends Roo.bootstrap.Component
7638  * Bootstrap TableBody class
7639  * @cfg {String} cls element class
7640  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7641  * @cfg {String} align Aligns the content inside the element
7642  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7643  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7644  * 
7645  * @constructor
7646  * Create a new TableBody
7647  * @param {Object} config The config object
7648  */
7649
7650 Roo.bootstrap.TableBody = function(config){
7651     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7652 };
7653
7654 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7655     
7656     cls: false,
7657     tag: false,
7658     align: false,
7659     charoff: false,
7660     valign: false,
7661     
7662     getAutoCreate : function(){
7663         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7664         
7665         cfg = {
7666             tag: 'tbody'
7667         };
7668             
7669         if (this.cls) {
7670             cfg.cls=this.cls
7671         }
7672         if(this.tag){
7673             cfg.tag = this.tag;
7674         }
7675         
7676         if(this.align){
7677             cfg.align = this.align;
7678         }
7679         if(this.charoff){
7680             cfg.charoff = this.charoff;
7681         }
7682         if(this.valign){
7683             cfg.valign = this.valign;
7684         }
7685         
7686         return cfg;
7687     }
7688     
7689     
7690 //    initEvents : function()
7691 //    {
7692 //        
7693 //        if(!this.store){
7694 //            return;
7695 //        }
7696 //        
7697 //        this.store = Roo.factory(this.store, Roo.data);
7698 //        this.store.on('load', this.onLoad, this);
7699 //        
7700 //        this.store.load();
7701 //        
7702 //    },
7703 //    
7704 //    onLoad: function () 
7705 //    {   
7706 //        this.fireEvent('load', this);
7707 //    }
7708 //    
7709 //   
7710 });
7711
7712  
7713
7714  /*
7715  * Based on:
7716  * Ext JS Library 1.1.1
7717  * Copyright(c) 2006-2007, Ext JS, LLC.
7718  *
7719  * Originally Released Under LGPL - original licence link has changed is not relivant.
7720  *
7721  * Fork - LGPL
7722  * <script type="text/javascript">
7723  */
7724
7725 // as we use this in bootstrap.
7726 Roo.namespace('Roo.form');
7727  /**
7728  * @class Roo.form.Action
7729  * Internal Class used to handle form actions
7730  * @constructor
7731  * @param {Roo.form.BasicForm} el The form element or its id
7732  * @param {Object} config Configuration options
7733  */
7734
7735  
7736  
7737 // define the action interface
7738 Roo.form.Action = function(form, options){
7739     this.form = form;
7740     this.options = options || {};
7741 };
7742 /**
7743  * Client Validation Failed
7744  * @const 
7745  */
7746 Roo.form.Action.CLIENT_INVALID = 'client';
7747 /**
7748  * Server Validation Failed
7749  * @const 
7750  */
7751 Roo.form.Action.SERVER_INVALID = 'server';
7752  /**
7753  * Connect to Server Failed
7754  * @const 
7755  */
7756 Roo.form.Action.CONNECT_FAILURE = 'connect';
7757 /**
7758  * Reading Data from Server Failed
7759  * @const 
7760  */
7761 Roo.form.Action.LOAD_FAILURE = 'load';
7762
7763 Roo.form.Action.prototype = {
7764     type : 'default',
7765     failureType : undefined,
7766     response : undefined,
7767     result : undefined,
7768
7769     // interface method
7770     run : function(options){
7771
7772     },
7773
7774     // interface method
7775     success : function(response){
7776
7777     },
7778
7779     // interface method
7780     handleResponse : function(response){
7781
7782     },
7783
7784     // default connection failure
7785     failure : function(response){
7786         
7787         this.response = response;
7788         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7789         this.form.afterAction(this, false);
7790     },
7791
7792     processResponse : function(response){
7793         this.response = response;
7794         if(!response.responseText){
7795             return true;
7796         }
7797         this.result = this.handleResponse(response);
7798         return this.result;
7799     },
7800
7801     // utility functions used internally
7802     getUrl : function(appendParams){
7803         var url = this.options.url || this.form.url || this.form.el.dom.action;
7804         if(appendParams){
7805             var p = this.getParams();
7806             if(p){
7807                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7808             }
7809         }
7810         return url;
7811     },
7812
7813     getMethod : function(){
7814         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7815     },
7816
7817     getParams : function(){
7818         var bp = this.form.baseParams;
7819         var p = this.options.params;
7820         if(p){
7821             if(typeof p == "object"){
7822                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7823             }else if(typeof p == 'string' && bp){
7824                 p += '&' + Roo.urlEncode(bp);
7825             }
7826         }else if(bp){
7827             p = Roo.urlEncode(bp);
7828         }
7829         return p;
7830     },
7831
7832     createCallback : function(){
7833         return {
7834             success: this.success,
7835             failure: this.failure,
7836             scope: this,
7837             timeout: (this.form.timeout*1000),
7838             upload: this.form.fileUpload ? this.success : undefined
7839         };
7840     }
7841 };
7842
7843 Roo.form.Action.Submit = function(form, options){
7844     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7845 };
7846
7847 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7848     type : 'submit',
7849
7850     haveProgress : false,
7851     uploadComplete : false,
7852     
7853     // uploadProgress indicator.
7854     uploadProgress : function()
7855     {
7856         if (!this.form.progressUrl) {
7857             return;
7858         }
7859         
7860         if (!this.haveProgress) {
7861             Roo.MessageBox.progress("Uploading", "Uploading");
7862         }
7863         if (this.uploadComplete) {
7864            Roo.MessageBox.hide();
7865            return;
7866         }
7867         
7868         this.haveProgress = true;
7869    
7870         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7871         
7872         var c = new Roo.data.Connection();
7873         c.request({
7874             url : this.form.progressUrl,
7875             params: {
7876                 id : uid
7877             },
7878             method: 'GET',
7879             success : function(req){
7880                //console.log(data);
7881                 var rdata = false;
7882                 var edata;
7883                 try  {
7884                    rdata = Roo.decode(req.responseText)
7885                 } catch (e) {
7886                     Roo.log("Invalid data from server..");
7887                     Roo.log(edata);
7888                     return;
7889                 }
7890                 if (!rdata || !rdata.success) {
7891                     Roo.log(rdata);
7892                     Roo.MessageBox.alert(Roo.encode(rdata));
7893                     return;
7894                 }
7895                 var data = rdata.data;
7896                 
7897                 if (this.uploadComplete) {
7898                    Roo.MessageBox.hide();
7899                    return;
7900                 }
7901                    
7902                 if (data){
7903                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7904                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7905                     );
7906                 }
7907                 this.uploadProgress.defer(2000,this);
7908             },
7909        
7910             failure: function(data) {
7911                 Roo.log('progress url failed ');
7912                 Roo.log(data);
7913             },
7914             scope : this
7915         });
7916            
7917     },
7918     
7919     
7920     run : function()
7921     {
7922         // run get Values on the form, so it syncs any secondary forms.
7923         this.form.getValues();
7924         
7925         var o = this.options;
7926         var method = this.getMethod();
7927         var isPost = method == 'POST';
7928         if(o.clientValidation === false || this.form.isValid()){
7929             
7930             if (this.form.progressUrl) {
7931                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7932                     (new Date() * 1) + '' + Math.random());
7933                     
7934             } 
7935             
7936             
7937             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7938                 form:this.form.el.dom,
7939                 url:this.getUrl(!isPost),
7940                 method: method,
7941                 params:isPost ? this.getParams() : null,
7942                 isUpload: this.form.fileUpload,
7943                 formData : this.form.formData
7944             }));
7945             
7946             this.uploadProgress();
7947
7948         }else if (o.clientValidation !== false){ // client validation failed
7949             this.failureType = Roo.form.Action.CLIENT_INVALID;
7950             this.form.afterAction(this, false);
7951         }
7952     },
7953
7954     success : function(response)
7955     {
7956         this.uploadComplete= true;
7957         if (this.haveProgress) {
7958             Roo.MessageBox.hide();
7959         }
7960         
7961         
7962         var result = this.processResponse(response);
7963         if(result === true || result.success){
7964             this.form.afterAction(this, true);
7965             return;
7966         }
7967         if(result.errors){
7968             this.form.markInvalid(result.errors);
7969             this.failureType = Roo.form.Action.SERVER_INVALID;
7970         }
7971         this.form.afterAction(this, false);
7972     },
7973     failure : function(response)
7974     {
7975         this.uploadComplete= true;
7976         if (this.haveProgress) {
7977             Roo.MessageBox.hide();
7978         }
7979         
7980         this.response = response;
7981         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7982         this.form.afterAction(this, false);
7983     },
7984     
7985     handleResponse : function(response){
7986         if(this.form.errorReader){
7987             var rs = this.form.errorReader.read(response);
7988             var errors = [];
7989             if(rs.records){
7990                 for(var i = 0, len = rs.records.length; i < len; i++) {
7991                     var r = rs.records[i];
7992                     errors[i] = r.data;
7993                 }
7994             }
7995             if(errors.length < 1){
7996                 errors = null;
7997             }
7998             return {
7999                 success : rs.success,
8000                 errors : errors
8001             };
8002         }
8003         var ret = false;
8004         try {
8005             ret = Roo.decode(response.responseText);
8006         } catch (e) {
8007             ret = {
8008                 success: false,
8009                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
8010                 errors : []
8011             };
8012         }
8013         return ret;
8014         
8015     }
8016 });
8017
8018
8019 Roo.form.Action.Load = function(form, options){
8020     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
8021     this.reader = this.form.reader;
8022 };
8023
8024 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
8025     type : 'load',
8026
8027     run : function(){
8028         
8029         Roo.Ajax.request(Roo.apply(
8030                 this.createCallback(), {
8031                     method:this.getMethod(),
8032                     url:this.getUrl(false),
8033                     params:this.getParams()
8034         }));
8035     },
8036
8037     success : function(response){
8038         
8039         var result = this.processResponse(response);
8040         if(result === true || !result.success || !result.data){
8041             this.failureType = Roo.form.Action.LOAD_FAILURE;
8042             this.form.afterAction(this, false);
8043             return;
8044         }
8045         this.form.clearInvalid();
8046         this.form.setValues(result.data);
8047         this.form.afterAction(this, true);
8048     },
8049
8050     handleResponse : function(response){
8051         if(this.form.reader){
8052             var rs = this.form.reader.read(response);
8053             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8054             return {
8055                 success : rs.success,
8056                 data : data
8057             };
8058         }
8059         return Roo.decode(response.responseText);
8060     }
8061 });
8062
8063 Roo.form.Action.ACTION_TYPES = {
8064     'load' : Roo.form.Action.Load,
8065     'submit' : Roo.form.Action.Submit
8066 };/*
8067  * - LGPL
8068  *
8069  * form
8070  *
8071  */
8072
8073 /**
8074  * @class Roo.bootstrap.Form
8075  * @extends Roo.bootstrap.Component
8076  * Bootstrap Form class
8077  * @cfg {String} method  GET | POST (default POST)
8078  * @cfg {String} labelAlign top | left (default top)
8079  * @cfg {String} align left  | right - for navbars
8080  * @cfg {Boolean} loadMask load mask when submit (default true)
8081
8082  *
8083  * @constructor
8084  * Create a new Form
8085  * @param {Object} config The config object
8086  */
8087
8088
8089 Roo.bootstrap.Form = function(config){
8090     
8091     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8092     
8093     Roo.bootstrap.Form.popover.apply();
8094     
8095     this.addEvents({
8096         /**
8097          * @event clientvalidation
8098          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8099          * @param {Form} this
8100          * @param {Boolean} valid true if the form has passed client-side validation
8101          */
8102         clientvalidation: true,
8103         /**
8104          * @event beforeaction
8105          * Fires before any action is performed. Return false to cancel the action.
8106          * @param {Form} this
8107          * @param {Action} action The action to be performed
8108          */
8109         beforeaction: true,
8110         /**
8111          * @event actionfailed
8112          * Fires when an action fails.
8113          * @param {Form} this
8114          * @param {Action} action The action that failed
8115          */
8116         actionfailed : true,
8117         /**
8118          * @event actioncomplete
8119          * Fires when an action is completed.
8120          * @param {Form} this
8121          * @param {Action} action The action that completed
8122          */
8123         actioncomplete : true
8124     });
8125 };
8126
8127 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8128
8129      /**
8130      * @cfg {String} method
8131      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8132      */
8133     method : 'POST',
8134     /**
8135      * @cfg {String} url
8136      * The URL to use for form actions if one isn't supplied in the action options.
8137      */
8138     /**
8139      * @cfg {Boolean} fileUpload
8140      * Set to true if this form is a file upload.
8141      */
8142
8143     /**
8144      * @cfg {Object} baseParams
8145      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8146      */
8147
8148     /**
8149      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8150      */
8151     timeout: 30,
8152     /**
8153      * @cfg {Sting} align (left|right) for navbar forms
8154      */
8155     align : 'left',
8156
8157     // private
8158     activeAction : null,
8159
8160     /**
8161      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8162      * element by passing it or its id or mask the form itself by passing in true.
8163      * @type Mixed
8164      */
8165     waitMsgTarget : false,
8166
8167     loadMask : true,
8168     
8169     /**
8170      * @cfg {Boolean} errorMask (true|false) default false
8171      */
8172     errorMask : false,
8173     
8174     /**
8175      * @cfg {Number} maskOffset Default 100
8176      */
8177     maskOffset : 100,
8178     
8179     /**
8180      * @cfg {Boolean} maskBody
8181      */
8182     maskBody : false,
8183
8184     getAutoCreate : function(){
8185
8186         var cfg = {
8187             tag: 'form',
8188             method : this.method || 'POST',
8189             id : this.id || Roo.id(),
8190             cls : ''
8191         };
8192         if (this.parent().xtype.match(/^Nav/)) {
8193             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8194
8195         }
8196
8197         if (this.labelAlign == 'left' ) {
8198             cfg.cls += ' form-horizontal';
8199         }
8200
8201
8202         return cfg;
8203     },
8204     initEvents : function()
8205     {
8206         this.el.on('submit', this.onSubmit, this);
8207         // this was added as random key presses on the form where triggering form submit.
8208         this.el.on('keypress', function(e) {
8209             if (e.getCharCode() != 13) {
8210                 return true;
8211             }
8212             // we might need to allow it for textareas.. and some other items.
8213             // check e.getTarget().
8214
8215             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8216                 return true;
8217             }
8218
8219             Roo.log("keypress blocked");
8220
8221             e.preventDefault();
8222             return false;
8223         });
8224         
8225     },
8226     // private
8227     onSubmit : function(e){
8228         e.stopEvent();
8229     },
8230
8231      /**
8232      * Returns true if client-side validation on the form is successful.
8233      * @return Boolean
8234      */
8235     isValid : function(){
8236         var items = this.getItems();
8237         var valid = true;
8238         var target = false;
8239         
8240         items.each(function(f){
8241             
8242             if(f.validate()){
8243                 return;
8244             }
8245             
8246             Roo.log('invalid field: ' + f.name);
8247             
8248             valid = false;
8249
8250             if(!target && f.el.isVisible(true)){
8251                 target = f;
8252             }
8253            
8254         });
8255         
8256         if(this.errorMask && !valid){
8257             Roo.bootstrap.Form.popover.mask(this, target);
8258         }
8259         
8260         return valid;
8261     },
8262     
8263     /**
8264      * Returns true if any fields in this form have changed since their original load.
8265      * @return Boolean
8266      */
8267     isDirty : function(){
8268         var dirty = false;
8269         var items = this.getItems();
8270         items.each(function(f){
8271            if(f.isDirty()){
8272                dirty = true;
8273                return false;
8274            }
8275            return true;
8276         });
8277         return dirty;
8278     },
8279      /**
8280      * Performs a predefined action (submit or load) or custom actions you define on this form.
8281      * @param {String} actionName The name of the action type
8282      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8283      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8284      * accept other config options):
8285      * <pre>
8286 Property          Type             Description
8287 ----------------  ---------------  ----------------------------------------------------------------------------------
8288 url               String           The url for the action (defaults to the form's url)
8289 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8290 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8291 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8292                                    validate the form on the client (defaults to false)
8293      * </pre>
8294      * @return {BasicForm} this
8295      */
8296     doAction : function(action, options){
8297         if(typeof action == 'string'){
8298             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8299         }
8300         if(this.fireEvent('beforeaction', this, action) !== false){
8301             this.beforeAction(action);
8302             action.run.defer(100, action);
8303         }
8304         return this;
8305     },
8306
8307     // private
8308     beforeAction : function(action){
8309         var o = action.options;
8310         
8311         if(this.loadMask){
8312             
8313             if(this.maskBody){
8314                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8315             } else {
8316                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8317             }
8318         }
8319         // not really supported yet.. ??
8320
8321         //if(this.waitMsgTarget === true){
8322         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8323         //}else if(this.waitMsgTarget){
8324         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8325         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8326         //}else {
8327         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8328        // }
8329
8330     },
8331
8332     // private
8333     afterAction : function(action, success){
8334         this.activeAction = null;
8335         var o = action.options;
8336
8337         if(this.loadMask){
8338             
8339             if(this.maskBody){
8340                 Roo.get(document.body).unmask();
8341             } else {
8342                 this.el.unmask();
8343             }
8344         }
8345         
8346         //if(this.waitMsgTarget === true){
8347 //            this.el.unmask();
8348         //}else if(this.waitMsgTarget){
8349         //    this.waitMsgTarget.unmask();
8350         //}else{
8351         //    Roo.MessageBox.updateProgress(1);
8352         //    Roo.MessageBox.hide();
8353        // }
8354         //
8355         if(success){
8356             if(o.reset){
8357                 this.reset();
8358             }
8359             Roo.callback(o.success, o.scope, [this, action]);
8360             this.fireEvent('actioncomplete', this, action);
8361
8362         }else{
8363
8364             // failure condition..
8365             // we have a scenario where updates need confirming.
8366             // eg. if a locking scenario exists..
8367             // we look for { errors : { needs_confirm : true }} in the response.
8368             if (
8369                 (typeof(action.result) != 'undefined')  &&
8370                 (typeof(action.result.errors) != 'undefined')  &&
8371                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8372            ){
8373                 var _t = this;
8374                 Roo.log("not supported yet");
8375                  /*
8376
8377                 Roo.MessageBox.confirm(
8378                     "Change requires confirmation",
8379                     action.result.errorMsg,
8380                     function(r) {
8381                         if (r != 'yes') {
8382                             return;
8383                         }
8384                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8385                     }
8386
8387                 );
8388                 */
8389
8390
8391                 return;
8392             }
8393
8394             Roo.callback(o.failure, o.scope, [this, action]);
8395             // show an error message if no failed handler is set..
8396             if (!this.hasListener('actionfailed')) {
8397                 Roo.log("need to add dialog support");
8398                 /*
8399                 Roo.MessageBox.alert("Error",
8400                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8401                         action.result.errorMsg :
8402                         "Saving Failed, please check your entries or try again"
8403                 );
8404                 */
8405             }
8406
8407             this.fireEvent('actionfailed', this, action);
8408         }
8409
8410     },
8411     /**
8412      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8413      * @param {String} id The value to search for
8414      * @return Field
8415      */
8416     findField : function(id){
8417         var items = this.getItems();
8418         var field = items.get(id);
8419         if(!field){
8420              items.each(function(f){
8421                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8422                     field = f;
8423                     return false;
8424                 }
8425                 return true;
8426             });
8427         }
8428         return field || null;
8429     },
8430      /**
8431      * Mark fields in this form invalid in bulk.
8432      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8433      * @return {BasicForm} this
8434      */
8435     markInvalid : function(errors){
8436         if(errors instanceof Array){
8437             for(var i = 0, len = errors.length; i < len; i++){
8438                 var fieldError = errors[i];
8439                 var f = this.findField(fieldError.id);
8440                 if(f){
8441                     f.markInvalid(fieldError.msg);
8442                 }
8443             }
8444         }else{
8445             var field, id;
8446             for(id in errors){
8447                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8448                     field.markInvalid(errors[id]);
8449                 }
8450             }
8451         }
8452         //Roo.each(this.childForms || [], function (f) {
8453         //    f.markInvalid(errors);
8454         //});
8455
8456         return this;
8457     },
8458
8459     /**
8460      * Set values for fields in this form in bulk.
8461      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8462      * @return {BasicForm} this
8463      */
8464     setValues : function(values){
8465         if(values instanceof Array){ // array of objects
8466             for(var i = 0, len = values.length; i < len; i++){
8467                 var v = values[i];
8468                 var f = this.findField(v.id);
8469                 if(f){
8470                     f.setValue(v.value);
8471                     if(this.trackResetOnLoad){
8472                         f.originalValue = f.getValue();
8473                     }
8474                 }
8475             }
8476         }else{ // object hash
8477             var field, id;
8478             for(id in values){
8479                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8480
8481                     if (field.setFromData &&
8482                         field.valueField &&
8483                         field.displayField &&
8484                         // combos' with local stores can
8485                         // be queried via setValue()
8486                         // to set their value..
8487                         (field.store && !field.store.isLocal)
8488                         ) {
8489                         // it's a combo
8490                         var sd = { };
8491                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8492                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8493                         field.setFromData(sd);
8494
8495                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8496                         
8497                         field.setFromData(values);
8498                         
8499                     } else {
8500                         field.setValue(values[id]);
8501                     }
8502
8503
8504                     if(this.trackResetOnLoad){
8505                         field.originalValue = field.getValue();
8506                     }
8507                 }
8508             }
8509         }
8510
8511         //Roo.each(this.childForms || [], function (f) {
8512         //    f.setValues(values);
8513         //});
8514
8515         return this;
8516     },
8517
8518     /**
8519      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8520      * they are returned as an array.
8521      * @param {Boolean} asString
8522      * @return {Object}
8523      */
8524     getValues : function(asString){
8525         //if (this.childForms) {
8526             // copy values from the child forms
8527         //    Roo.each(this.childForms, function (f) {
8528         //        this.setValues(f.getValues());
8529         //    }, this);
8530         //}
8531
8532
8533
8534         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8535         if(asString === true){
8536             return fs;
8537         }
8538         return Roo.urlDecode(fs);
8539     },
8540
8541     /**
8542      * Returns the fields in this form as an object with key/value pairs.
8543      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8544      * @return {Object}
8545      */
8546     getFieldValues : function(with_hidden)
8547     {
8548         var items = this.getItems();
8549         var ret = {};
8550         items.each(function(f){
8551             
8552             if (!f.getName()) {
8553                 return;
8554             }
8555             
8556             var v = f.getValue();
8557             
8558             if (f.inputType =='radio') {
8559                 if (typeof(ret[f.getName()]) == 'undefined') {
8560                     ret[f.getName()] = ''; // empty..
8561                 }
8562
8563                 if (!f.el.dom.checked) {
8564                     return;
8565
8566                 }
8567                 v = f.el.dom.value;
8568
8569             }
8570             
8571             if(f.xtype == 'MoneyField'){
8572                 ret[f.currencyName] = f.getCurrency();
8573             }
8574
8575             // not sure if this supported any more..
8576             if ((typeof(v) == 'object') && f.getRawValue) {
8577                 v = f.getRawValue() ; // dates..
8578             }
8579             // combo boxes where name != hiddenName...
8580             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8581                 ret[f.name] = f.getRawValue();
8582             }
8583             ret[f.getName()] = v;
8584         });
8585
8586         return ret;
8587     },
8588
8589     /**
8590      * Clears all invalid messages in this form.
8591      * @return {BasicForm} this
8592      */
8593     clearInvalid : function(){
8594         var items = this.getItems();
8595
8596         items.each(function(f){
8597            f.clearInvalid();
8598         });
8599
8600         return this;
8601     },
8602
8603     /**
8604      * Resets this form.
8605      * @return {BasicForm} this
8606      */
8607     reset : function(){
8608         var items = this.getItems();
8609         items.each(function(f){
8610             f.reset();
8611         });
8612
8613         Roo.each(this.childForms || [], function (f) {
8614             f.reset();
8615         });
8616
8617
8618         return this;
8619     },
8620     
8621     getItems : function()
8622     {
8623         var r=new Roo.util.MixedCollection(false, function(o){
8624             return o.id || (o.id = Roo.id());
8625         });
8626         var iter = function(el) {
8627             if (el.inputEl) {
8628                 r.add(el);
8629             }
8630             if (!el.items) {
8631                 return;
8632             }
8633             Roo.each(el.items,function(e) {
8634                 iter(e);
8635             });
8636         };
8637
8638         iter(this);
8639         return r;
8640     },
8641     
8642     hideFields : function(items)
8643     {
8644         Roo.each(items, function(i){
8645             
8646             var f = this.findField(i);
8647             
8648             if(!f){
8649                 return;
8650             }
8651             
8652             f.hide();
8653             
8654         }, this);
8655     },
8656     
8657     showFields : function(items)
8658     {
8659         Roo.each(items, function(i){
8660             
8661             var f = this.findField(i);
8662             
8663             if(!f){
8664                 return;
8665             }
8666             
8667             f.show();
8668             
8669         }, this);
8670     }
8671
8672 });
8673
8674 Roo.apply(Roo.bootstrap.Form, {
8675     
8676     popover : {
8677         
8678         padding : 5,
8679         
8680         isApplied : false,
8681         
8682         isMasked : false,
8683         
8684         form : false,
8685         
8686         target : false,
8687         
8688         toolTip : false,
8689         
8690         intervalID : false,
8691         
8692         maskEl : false,
8693         
8694         apply : function()
8695         {
8696             if(this.isApplied){
8697                 return;
8698             }
8699             
8700             this.maskEl = {
8701                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8702                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8703                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8704                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8705             };
8706             
8707             this.maskEl.top.enableDisplayMode("block");
8708             this.maskEl.left.enableDisplayMode("block");
8709             this.maskEl.bottom.enableDisplayMode("block");
8710             this.maskEl.right.enableDisplayMode("block");
8711             
8712             this.toolTip = new Roo.bootstrap.Tooltip({
8713                 cls : 'roo-form-error-popover',
8714                 alignment : {
8715                     'left' : ['r-l', [-2,0], 'right'],
8716                     'right' : ['l-r', [2,0], 'left'],
8717                     'bottom' : ['tl-bl', [0,2], 'top'],
8718                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8719                 }
8720             });
8721             
8722             this.toolTip.render(Roo.get(document.body));
8723
8724             this.toolTip.el.enableDisplayMode("block");
8725             
8726             Roo.get(document.body).on('click', function(){
8727                 this.unmask();
8728             }, this);
8729             
8730             Roo.get(document.body).on('touchstart', function(){
8731                 this.unmask();
8732             }, this);
8733             
8734             this.isApplied = true
8735         },
8736         
8737         mask : function(form, target)
8738         {
8739             this.form = form;
8740             
8741             this.target = target;
8742             
8743             if(!this.form.errorMask || !target.el){
8744                 return;
8745             }
8746             
8747             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8748             
8749             Roo.log(scrollable);
8750             
8751             var ot = this.target.el.calcOffsetsTo(scrollable);
8752             
8753             var scrollTo = ot[1] - this.form.maskOffset;
8754             
8755             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8756             
8757             scrollable.scrollTo('top', scrollTo);
8758             
8759             var box = this.target.el.getBox();
8760             Roo.log(box);
8761             var zIndex = Roo.bootstrap.Modal.zIndex++;
8762
8763             
8764             this.maskEl.top.setStyle('position', 'absolute');
8765             this.maskEl.top.setStyle('z-index', zIndex);
8766             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8767             this.maskEl.top.setLeft(0);
8768             this.maskEl.top.setTop(0);
8769             this.maskEl.top.show();
8770             
8771             this.maskEl.left.setStyle('position', 'absolute');
8772             this.maskEl.left.setStyle('z-index', zIndex);
8773             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8774             this.maskEl.left.setLeft(0);
8775             this.maskEl.left.setTop(box.y - this.padding);
8776             this.maskEl.left.show();
8777
8778             this.maskEl.bottom.setStyle('position', 'absolute');
8779             this.maskEl.bottom.setStyle('z-index', zIndex);
8780             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8781             this.maskEl.bottom.setLeft(0);
8782             this.maskEl.bottom.setTop(box.bottom + this.padding);
8783             this.maskEl.bottom.show();
8784
8785             this.maskEl.right.setStyle('position', 'absolute');
8786             this.maskEl.right.setStyle('z-index', zIndex);
8787             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8788             this.maskEl.right.setLeft(box.right + this.padding);
8789             this.maskEl.right.setTop(box.y - this.padding);
8790             this.maskEl.right.show();
8791
8792             this.toolTip.bindEl = this.target.el;
8793
8794             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8795
8796             var tip = this.target.blankText;
8797
8798             if(this.target.getValue() !== '' ) {
8799                 
8800                 if (this.target.invalidText.length) {
8801                     tip = this.target.invalidText;
8802                 } else if (this.target.regexText.length){
8803                     tip = this.target.regexText;
8804                 }
8805             }
8806
8807             this.toolTip.show(tip);
8808
8809             this.intervalID = window.setInterval(function() {
8810                 Roo.bootstrap.Form.popover.unmask();
8811             }, 10000);
8812
8813             window.onwheel = function(){ return false;};
8814             
8815             (function(){ this.isMasked = true; }).defer(500, this);
8816             
8817         },
8818         
8819         unmask : function()
8820         {
8821             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8822                 return;
8823             }
8824             
8825             this.maskEl.top.setStyle('position', 'absolute');
8826             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8827             this.maskEl.top.hide();
8828
8829             this.maskEl.left.setStyle('position', 'absolute');
8830             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8831             this.maskEl.left.hide();
8832
8833             this.maskEl.bottom.setStyle('position', 'absolute');
8834             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8835             this.maskEl.bottom.hide();
8836
8837             this.maskEl.right.setStyle('position', 'absolute');
8838             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8839             this.maskEl.right.hide();
8840             
8841             this.toolTip.hide();
8842             
8843             this.toolTip.el.hide();
8844             
8845             window.onwheel = function(){ return true;};
8846             
8847             if(this.intervalID){
8848                 window.clearInterval(this.intervalID);
8849                 this.intervalID = false;
8850             }
8851             
8852             this.isMasked = false;
8853             
8854         }
8855         
8856     }
8857     
8858 });
8859
8860 /*
8861  * Based on:
8862  * Ext JS Library 1.1.1
8863  * Copyright(c) 2006-2007, Ext JS, LLC.
8864  *
8865  * Originally Released Under LGPL - original licence link has changed is not relivant.
8866  *
8867  * Fork - LGPL
8868  * <script type="text/javascript">
8869  */
8870 /**
8871  * @class Roo.form.VTypes
8872  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8873  * @singleton
8874  */
8875 Roo.form.VTypes = function(){
8876     // closure these in so they are only created once.
8877     var alpha = /^[a-zA-Z_]+$/;
8878     var alphanum = /^[a-zA-Z0-9_]+$/;
8879     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8880     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8881
8882     // All these messages and functions are configurable
8883     return {
8884         /**
8885          * The function used to validate email addresses
8886          * @param {String} value The email address
8887          */
8888         'email' : function(v){
8889             return email.test(v);
8890         },
8891         /**
8892          * The error text to display when the email validation function returns false
8893          * @type String
8894          */
8895         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8896         /**
8897          * The keystroke filter mask to be applied on email input
8898          * @type RegExp
8899          */
8900         'emailMask' : /[a-z0-9_\.\-@]/i,
8901
8902         /**
8903          * The function used to validate URLs
8904          * @param {String} value The URL
8905          */
8906         'url' : function(v){
8907             return url.test(v);
8908         },
8909         /**
8910          * The error text to display when the url validation function returns false
8911          * @type String
8912          */
8913         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8914         
8915         /**
8916          * The function used to validate alpha values
8917          * @param {String} value The value
8918          */
8919         'alpha' : function(v){
8920             return alpha.test(v);
8921         },
8922         /**
8923          * The error text to display when the alpha validation function returns false
8924          * @type String
8925          */
8926         'alphaText' : 'This field should only contain letters and _',
8927         /**
8928          * The keystroke filter mask to be applied on alpha input
8929          * @type RegExp
8930          */
8931         'alphaMask' : /[a-z_]/i,
8932
8933         /**
8934          * The function used to validate alphanumeric values
8935          * @param {String} value The value
8936          */
8937         'alphanum' : function(v){
8938             return alphanum.test(v);
8939         },
8940         /**
8941          * The error text to display when the alphanumeric validation function returns false
8942          * @type String
8943          */
8944         'alphanumText' : 'This field should only contain letters, numbers and _',
8945         /**
8946          * The keystroke filter mask to be applied on alphanumeric input
8947          * @type RegExp
8948          */
8949         'alphanumMask' : /[a-z0-9_]/i
8950     };
8951 }();/*
8952  * - LGPL
8953  *
8954  * Input
8955  * 
8956  */
8957
8958 /**
8959  * @class Roo.bootstrap.Input
8960  * @extends Roo.bootstrap.Component
8961  * Bootstrap Input class
8962  * @cfg {Boolean} disabled is it disabled
8963  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8964  * @cfg {String} name name of the input
8965  * @cfg {string} fieldLabel - the label associated
8966  * @cfg {string} placeholder - placeholder to put in text.
8967  * @cfg {string}  before - input group add on before
8968  * @cfg {string} after - input group add on after
8969  * @cfg {string} size - (lg|sm) or leave empty..
8970  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8971  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8972  * @cfg {Number} md colspan out of 12 for computer-sized screens
8973  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8974  * @cfg {string} value default value of the input
8975  * @cfg {Number} labelWidth set the width of label 
8976  * @cfg {Number} labellg set the width of label (1-12)
8977  * @cfg {Number} labelmd set the width of label (1-12)
8978  * @cfg {Number} labelsm set the width of label (1-12)
8979  * @cfg {Number} labelxs set the width of label (1-12)
8980  * @cfg {String} labelAlign (top|left)
8981  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8982  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8983  * @cfg {String} indicatorpos (left|right) default left
8984  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8985  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8986
8987  * @cfg {String} align (left|center|right) Default left
8988  * @cfg {Boolean} forceFeedback (true|false) Default false
8989  * 
8990  * @constructor
8991  * Create a new Input
8992  * @param {Object} config The config object
8993  */
8994
8995 Roo.bootstrap.Input = function(config){
8996     
8997     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8998     
8999     this.addEvents({
9000         /**
9001          * @event focus
9002          * Fires when this field receives input focus.
9003          * @param {Roo.form.Field} this
9004          */
9005         focus : true,
9006         /**
9007          * @event blur
9008          * Fires when this field loses input focus.
9009          * @param {Roo.form.Field} this
9010          */
9011         blur : true,
9012         /**
9013          * @event specialkey
9014          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9015          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9016          * @param {Roo.form.Field} this
9017          * @param {Roo.EventObject} e The event object
9018          */
9019         specialkey : true,
9020         /**
9021          * @event change
9022          * Fires just before the field blurs if the field value has changed.
9023          * @param {Roo.form.Field} this
9024          * @param {Mixed} newValue The new value
9025          * @param {Mixed} oldValue The original value
9026          */
9027         change : true,
9028         /**
9029          * @event invalid
9030          * Fires after the field has been marked as invalid.
9031          * @param {Roo.form.Field} this
9032          * @param {String} msg The validation message
9033          */
9034         invalid : true,
9035         /**
9036          * @event valid
9037          * Fires after the field has been validated with no errors.
9038          * @param {Roo.form.Field} this
9039          */
9040         valid : true,
9041          /**
9042          * @event keyup
9043          * Fires after the key up
9044          * @param {Roo.form.Field} this
9045          * @param {Roo.EventObject}  e The event Object
9046          */
9047         keyup : true
9048     });
9049 };
9050
9051 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9052      /**
9053      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9054       automatic validation (defaults to "keyup").
9055      */
9056     validationEvent : "keyup",
9057      /**
9058      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9059      */
9060     validateOnBlur : true,
9061     /**
9062      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9063      */
9064     validationDelay : 250,
9065      /**
9066      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9067      */
9068     focusClass : "x-form-focus",  // not needed???
9069     
9070        
9071     /**
9072      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9073      */
9074     invalidClass : "has-warning",
9075     
9076     /**
9077      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9078      */
9079     validClass : "has-success",
9080     
9081     /**
9082      * @cfg {Boolean} hasFeedback (true|false) default true
9083      */
9084     hasFeedback : true,
9085     
9086     /**
9087      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9088      */
9089     invalidFeedbackClass : "glyphicon-warning-sign",
9090     
9091     /**
9092      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9093      */
9094     validFeedbackClass : "glyphicon-ok",
9095     
9096     /**
9097      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9098      */
9099     selectOnFocus : false,
9100     
9101      /**
9102      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9103      */
9104     maskRe : null,
9105        /**
9106      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9107      */
9108     vtype : null,
9109     
9110       /**
9111      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9112      */
9113     disableKeyFilter : false,
9114     
9115        /**
9116      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9117      */
9118     disabled : false,
9119      /**
9120      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9121      */
9122     allowBlank : true,
9123     /**
9124      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9125      */
9126     blankText : "Please complete this mandatory field",
9127     
9128      /**
9129      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9130      */
9131     minLength : 0,
9132     /**
9133      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9134      */
9135     maxLength : Number.MAX_VALUE,
9136     /**
9137      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9138      */
9139     minLengthText : "The minimum length for this field is {0}",
9140     /**
9141      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9142      */
9143     maxLengthText : "The maximum length for this field is {0}",
9144   
9145     
9146     /**
9147      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9148      * If available, this function will be called only after the basic validators all return true, and will be passed the
9149      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9150      */
9151     validator : null,
9152     /**
9153      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9154      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9155      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9156      */
9157     regex : null,
9158     /**
9159      * @cfg {String} regexText -- Depricated - use Invalid Text
9160      */
9161     regexText : "",
9162     
9163     /**
9164      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9165      */
9166     invalidText : "",
9167     
9168     
9169     
9170     autocomplete: false,
9171     
9172     
9173     fieldLabel : '',
9174     inputType : 'text',
9175     
9176     name : false,
9177     placeholder: false,
9178     before : false,
9179     after : false,
9180     size : false,
9181     hasFocus : false,
9182     preventMark: false,
9183     isFormField : true,
9184     value : '',
9185     labelWidth : 2,
9186     labelAlign : false,
9187     readOnly : false,
9188     align : false,
9189     formatedValue : false,
9190     forceFeedback : false,
9191     
9192     indicatorpos : 'left',
9193     
9194     labellg : 0,
9195     labelmd : 0,
9196     labelsm : 0,
9197     labelxs : 0,
9198     
9199     capture : '',
9200     accept : '',
9201     
9202     parentLabelAlign : function()
9203     {
9204         var parent = this;
9205         while (parent.parent()) {
9206             parent = parent.parent();
9207             if (typeof(parent.labelAlign) !='undefined') {
9208                 return parent.labelAlign;
9209             }
9210         }
9211         return 'left';
9212         
9213     },
9214     
9215     getAutoCreate : function()
9216     {
9217         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9218         
9219         var id = Roo.id();
9220         
9221         var cfg = {};
9222         
9223         if(this.inputType != 'hidden'){
9224             cfg.cls = 'form-group' //input-group
9225         }
9226         
9227         var input =  {
9228             tag: 'input',
9229             id : id,
9230             type : this.inputType,
9231             value : this.value,
9232             cls : 'form-control',
9233             placeholder : this.placeholder || '',
9234             autocomplete : this.autocomplete || 'new-password'
9235         };
9236         
9237         if(this.capture.length){
9238             input.capture = this.capture;
9239         }
9240         
9241         if(this.accept.length){
9242             input.accept = this.accept + "/*";
9243         }
9244         
9245         if(this.align){
9246             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9247         }
9248         
9249         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9250             input.maxLength = this.maxLength;
9251         }
9252         
9253         if (this.disabled) {
9254             input.disabled=true;
9255         }
9256         
9257         if (this.readOnly) {
9258             input.readonly=true;
9259         }
9260         
9261         if (this.name) {
9262             input.name = this.name;
9263         }
9264         
9265         if (this.size) {
9266             input.cls += ' input-' + this.size;
9267         }
9268         
9269         var settings=this;
9270         ['xs','sm','md','lg'].map(function(size){
9271             if (settings[size]) {
9272                 cfg.cls += ' col-' + size + '-' + settings[size];
9273             }
9274         });
9275         
9276         var inputblock = input;
9277         
9278         var feedback = {
9279             tag: 'span',
9280             cls: 'glyphicon form-control-feedback'
9281         };
9282             
9283         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9284             
9285             inputblock = {
9286                 cls : 'has-feedback',
9287                 cn :  [
9288                     input,
9289                     feedback
9290                 ] 
9291             };  
9292         }
9293         
9294         if (this.before || this.after) {
9295             
9296             inputblock = {
9297                 cls : 'input-group',
9298                 cn :  [] 
9299             };
9300             
9301             if (this.before && typeof(this.before) == 'string') {
9302                 
9303                 inputblock.cn.push({
9304                     tag :'span',
9305                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9306                     html : this.before
9307                 });
9308             }
9309             if (this.before && typeof(this.before) == 'object') {
9310                 this.before = Roo.factory(this.before);
9311                 
9312                 inputblock.cn.push({
9313                     tag :'span',
9314                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9315                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9316                 });
9317             }
9318             
9319             inputblock.cn.push(input);
9320             
9321             if (this.after && typeof(this.after) == 'string') {
9322                 inputblock.cn.push({
9323                     tag :'span',
9324                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9325                     html : this.after
9326                 });
9327             }
9328             if (this.after && typeof(this.after) == 'object') {
9329                 this.after = Roo.factory(this.after);
9330                 
9331                 inputblock.cn.push({
9332                     tag :'span',
9333                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9334                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9335                 });
9336             }
9337             
9338             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9339                 inputblock.cls += ' has-feedback';
9340                 inputblock.cn.push(feedback);
9341             }
9342         };
9343         var indicator = {
9344             tag : 'i',
9345             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9346             tooltip : 'This field is required'
9347         };
9348         if (Roo.bootstrap.version == 4) {
9349             indicator = {
9350                 tag : 'i',
9351                 style : 'display-none'
9352             };
9353         }
9354         if (align ==='left' && this.fieldLabel.length) {
9355             
9356             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9357             
9358             cfg.cn = [
9359                 indicator,
9360                 {
9361                     tag: 'label',
9362                     'for' :  id,
9363                     cls : 'control-label col-form-label',
9364                     html : this.fieldLabel
9365
9366                 },
9367                 {
9368                     cls : "", 
9369                     cn: [
9370                         inputblock
9371                     ]
9372                 }
9373             ];
9374             
9375             var labelCfg = cfg.cn[1];
9376             var contentCfg = cfg.cn[2];
9377             
9378             if(this.indicatorpos == 'right'){
9379                 cfg.cn = [
9380                     {
9381                         tag: 'label',
9382                         'for' :  id,
9383                         cls : 'control-label col-form-label',
9384                         cn : [
9385                             {
9386                                 tag : 'span',
9387                                 html : this.fieldLabel
9388                             },
9389                             indicator
9390                         ]
9391                     },
9392                     {
9393                         cls : "",
9394                         cn: [
9395                             inputblock
9396                         ]
9397                     }
9398
9399                 ];
9400                 
9401                 labelCfg = cfg.cn[0];
9402                 contentCfg = cfg.cn[1];
9403             
9404             }
9405             
9406             if(this.labelWidth > 12){
9407                 labelCfg.style = "width: " + this.labelWidth + 'px';
9408             }
9409             
9410             if(this.labelWidth < 13 && this.labelmd == 0){
9411                 this.labelmd = this.labelWidth;
9412             }
9413             
9414             if(this.labellg > 0){
9415                 labelCfg.cls += ' col-lg-' + this.labellg;
9416                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9417             }
9418             
9419             if(this.labelmd > 0){
9420                 labelCfg.cls += ' col-md-' + this.labelmd;
9421                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9422             }
9423             
9424             if(this.labelsm > 0){
9425                 labelCfg.cls += ' col-sm-' + this.labelsm;
9426                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9427             }
9428             
9429             if(this.labelxs > 0){
9430                 labelCfg.cls += ' col-xs-' + this.labelxs;
9431                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9432             }
9433             
9434             
9435         } else if ( this.fieldLabel.length) {
9436                 
9437             cfg.cn = [
9438                 {
9439                     tag : 'i',
9440                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9441                     tooltip : 'This field is required'
9442                 },
9443                 {
9444                     tag: 'label',
9445                    //cls : 'input-group-addon',
9446                     html : this.fieldLabel
9447
9448                 },
9449
9450                inputblock
9451
9452            ];
9453            
9454            if(this.indicatorpos == 'right'){
9455                 
9456                 cfg.cn = [
9457                     {
9458                         tag: 'label',
9459                        //cls : 'input-group-addon',
9460                         html : this.fieldLabel
9461
9462                     },
9463                     {
9464                         tag : 'i',
9465                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9466                         tooltip : 'This field is required'
9467                     },
9468
9469                    inputblock
9470
9471                ];
9472
9473             }
9474
9475         } else {
9476             
9477             cfg.cn = [
9478
9479                     inputblock
9480
9481             ];
9482                 
9483                 
9484         };
9485         
9486         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9487            cfg.cls += ' navbar-form';
9488         }
9489         
9490         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9491             // on BS4 we do this only if not form 
9492             cfg.cls += ' navbar-form';
9493             cfg.tag = 'li';
9494         }
9495         
9496         return cfg;
9497         
9498     },
9499     /**
9500      * return the real input element.
9501      */
9502     inputEl: function ()
9503     {
9504         return this.el.select('input.form-control',true).first();
9505     },
9506     
9507     tooltipEl : function()
9508     {
9509         return this.inputEl();
9510     },
9511     
9512     indicatorEl : function()
9513     {
9514         if (Roo.bootstrap.version == 4) {
9515             return false; // not enabled in v4 yet.
9516         }
9517         
9518         var indicator = this.el.select('i.roo-required-indicator',true).first();
9519         
9520         if(!indicator){
9521             return false;
9522         }
9523         
9524         return indicator;
9525         
9526     },
9527     
9528     setDisabled : function(v)
9529     {
9530         var i  = this.inputEl().dom;
9531         if (!v) {
9532             i.removeAttribute('disabled');
9533             return;
9534             
9535         }
9536         i.setAttribute('disabled','true');
9537     },
9538     initEvents : function()
9539     {
9540           
9541         this.inputEl().on("keydown" , this.fireKey,  this);
9542         this.inputEl().on("focus", this.onFocus,  this);
9543         this.inputEl().on("blur", this.onBlur,  this);
9544         
9545         this.inputEl().relayEvent('keyup', this);
9546         
9547         this.indicator = this.indicatorEl();
9548         
9549         if(this.indicator){
9550             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9551         }
9552  
9553         // reference to original value for reset
9554         this.originalValue = this.getValue();
9555         //Roo.form.TextField.superclass.initEvents.call(this);
9556         if(this.validationEvent == 'keyup'){
9557             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9558             this.inputEl().on('keyup', this.filterValidation, this);
9559         }
9560         else if(this.validationEvent !== false){
9561             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9562         }
9563         
9564         if(this.selectOnFocus){
9565             this.on("focus", this.preFocus, this);
9566             
9567         }
9568         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9569             this.inputEl().on("keypress", this.filterKeys, this);
9570         } else {
9571             this.inputEl().relayEvent('keypress', this);
9572         }
9573        /* if(this.grow){
9574             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9575             this.el.on("click", this.autoSize,  this);
9576         }
9577         */
9578         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9579             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9580         }
9581         
9582         if (typeof(this.before) == 'object') {
9583             this.before.render(this.el.select('.roo-input-before',true).first());
9584         }
9585         if (typeof(this.after) == 'object') {
9586             this.after.render(this.el.select('.roo-input-after',true).first());
9587         }
9588         
9589         this.inputEl().on('change', this.onChange, this);
9590         
9591     },
9592     filterValidation : function(e){
9593         if(!e.isNavKeyPress()){
9594             this.validationTask.delay(this.validationDelay);
9595         }
9596     },
9597      /**
9598      * Validates the field value
9599      * @return {Boolean} True if the value is valid, else false
9600      */
9601     validate : function(){
9602         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9603         if(this.disabled || this.validateValue(this.getRawValue())){
9604             this.markValid();
9605             return true;
9606         }
9607         
9608         this.markInvalid();
9609         return false;
9610     },
9611     
9612     
9613     /**
9614      * Validates a value according to the field's validation rules and marks the field as invalid
9615      * if the validation fails
9616      * @param {Mixed} value The value to validate
9617      * @return {Boolean} True if the value is valid, else false
9618      */
9619     validateValue : function(value)
9620     {
9621         if(this.getVisibilityEl().hasClass('hidden')){
9622             return true;
9623         }
9624         
9625         if(value.length < 1)  { // if it's blank
9626             if(this.allowBlank){
9627                 return true;
9628             }
9629             return false;
9630         }
9631         
9632         if(value.length < this.minLength){
9633             return false;
9634         }
9635         if(value.length > this.maxLength){
9636             return false;
9637         }
9638         if(this.vtype){
9639             var vt = Roo.form.VTypes;
9640             if(!vt[this.vtype](value, this)){
9641                 return false;
9642             }
9643         }
9644         if(typeof this.validator == "function"){
9645             var msg = this.validator(value);
9646             if(msg !== true){
9647                 return false;
9648             }
9649             if (typeof(msg) == 'string') {
9650                 this.invalidText = msg;
9651             }
9652         }
9653         
9654         if(this.regex && !this.regex.test(value)){
9655             return false;
9656         }
9657         
9658         return true;
9659     },
9660     
9661      // private
9662     fireKey : function(e){
9663         //Roo.log('field ' + e.getKey());
9664         if(e.isNavKeyPress()){
9665             this.fireEvent("specialkey", this, e);
9666         }
9667     },
9668     focus : function (selectText){
9669         if(this.rendered){
9670             this.inputEl().focus();
9671             if(selectText === true){
9672                 this.inputEl().dom.select();
9673             }
9674         }
9675         return this;
9676     } ,
9677     
9678     onFocus : function(){
9679         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9680            // this.el.addClass(this.focusClass);
9681         }
9682         if(!this.hasFocus){
9683             this.hasFocus = true;
9684             this.startValue = this.getValue();
9685             this.fireEvent("focus", this);
9686         }
9687     },
9688     
9689     beforeBlur : Roo.emptyFn,
9690
9691     
9692     // private
9693     onBlur : function(){
9694         this.beforeBlur();
9695         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9696             //this.el.removeClass(this.focusClass);
9697         }
9698         this.hasFocus = false;
9699         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9700             this.validate();
9701         }
9702         var v = this.getValue();
9703         if(String(v) !== String(this.startValue)){
9704             this.fireEvent('change', this, v, this.startValue);
9705         }
9706         this.fireEvent("blur", this);
9707     },
9708     
9709     onChange : function(e)
9710     {
9711         var v = this.getValue();
9712         if(String(v) !== String(this.startValue)){
9713             this.fireEvent('change', this, v, this.startValue);
9714         }
9715         
9716     },
9717     
9718     /**
9719      * Resets the current field value to the originally loaded value and clears any validation messages
9720      */
9721     reset : function(){
9722         this.setValue(this.originalValue);
9723         this.validate();
9724     },
9725      /**
9726      * Returns the name of the field
9727      * @return {Mixed} name The name field
9728      */
9729     getName: function(){
9730         return this.name;
9731     },
9732      /**
9733      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9734      * @return {Mixed} value The field value
9735      */
9736     getValue : function(){
9737         
9738         var v = this.inputEl().getValue();
9739         
9740         return v;
9741     },
9742     /**
9743      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9744      * @return {Mixed} value The field value
9745      */
9746     getRawValue : function(){
9747         var v = this.inputEl().getValue();
9748         
9749         return v;
9750     },
9751     
9752     /**
9753      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9754      * @param {Mixed} value The value to set
9755      */
9756     setRawValue : function(v){
9757         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9758     },
9759     
9760     selectText : function(start, end){
9761         var v = this.getRawValue();
9762         if(v.length > 0){
9763             start = start === undefined ? 0 : start;
9764             end = end === undefined ? v.length : end;
9765             var d = this.inputEl().dom;
9766             if(d.setSelectionRange){
9767                 d.setSelectionRange(start, end);
9768             }else if(d.createTextRange){
9769                 var range = d.createTextRange();
9770                 range.moveStart("character", start);
9771                 range.moveEnd("character", v.length-end);
9772                 range.select();
9773             }
9774         }
9775     },
9776     
9777     /**
9778      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9779      * @param {Mixed} value The value to set
9780      */
9781     setValue : function(v){
9782         this.value = v;
9783         if(this.rendered){
9784             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9785             this.validate();
9786         }
9787     },
9788     
9789     /*
9790     processValue : function(value){
9791         if(this.stripCharsRe){
9792             var newValue = value.replace(this.stripCharsRe, '');
9793             if(newValue !== value){
9794                 this.setRawValue(newValue);
9795                 return newValue;
9796             }
9797         }
9798         return value;
9799     },
9800   */
9801     preFocus : function(){
9802         
9803         if(this.selectOnFocus){
9804             this.inputEl().dom.select();
9805         }
9806     },
9807     filterKeys : function(e){
9808         var k = e.getKey();
9809         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9810             return;
9811         }
9812         var c = e.getCharCode(), cc = String.fromCharCode(c);
9813         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9814             return;
9815         }
9816         if(!this.maskRe.test(cc)){
9817             e.stopEvent();
9818         }
9819     },
9820      /**
9821      * Clear any invalid styles/messages for this field
9822      */
9823     clearInvalid : function(){
9824         
9825         if(!this.el || this.preventMark){ // not rendered
9826             return;
9827         }
9828         
9829         
9830         this.el.removeClass([this.invalidClass, 'is-invalid']);
9831         
9832         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9833             
9834             var feedback = this.el.select('.form-control-feedback', true).first();
9835             
9836             if(feedback){
9837                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9838             }
9839             
9840         }
9841         
9842         if(this.indicator){
9843             this.indicator.removeClass('visible');
9844             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9845         }
9846         
9847         this.fireEvent('valid', this);
9848     },
9849     
9850      /**
9851      * Mark this field as valid
9852      */
9853     markValid : function()
9854     {
9855         if(!this.el  || this.preventMark){ // not rendered...
9856             return;
9857         }
9858         
9859         this.el.removeClass([this.invalidClass, this.validClass]);
9860         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9861
9862         var feedback = this.el.select('.form-control-feedback', true).first();
9863             
9864         if(feedback){
9865             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9866         }
9867         
9868         if(this.indicator){
9869             this.indicator.removeClass('visible');
9870             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9871         }
9872         
9873         if(this.disabled){
9874             return;
9875         }
9876         
9877         if(this.allowBlank && !this.getRawValue().length){
9878             return;
9879         }
9880         if (Roo.bootstrap.version == 3) {
9881             this.el.addClass(this.validClass);
9882         } else {
9883             this.inputEl().addClass('is-valid');
9884         }
9885
9886         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9887             
9888             var feedback = this.el.select('.form-control-feedback', true).first();
9889             
9890             if(feedback){
9891                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9892                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9893             }
9894             
9895         }
9896         
9897         this.fireEvent('valid', this);
9898     },
9899     
9900      /**
9901      * Mark this field as invalid
9902      * @param {String} msg The validation message
9903      */
9904     markInvalid : function(msg)
9905     {
9906         if(!this.el  || this.preventMark){ // not rendered
9907             return;
9908         }
9909         
9910         this.el.removeClass([this.invalidClass, this.validClass]);
9911         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9912         
9913         var feedback = this.el.select('.form-control-feedback', true).first();
9914             
9915         if(feedback){
9916             this.el.select('.form-control-feedback', true).first().removeClass(
9917                     [this.invalidFeedbackClass, this.validFeedbackClass]);
9918         }
9919
9920         if(this.disabled){
9921             return;
9922         }
9923         
9924         if(this.allowBlank && !this.getRawValue().length){
9925             return;
9926         }
9927         
9928         if(this.indicator){
9929             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9930             this.indicator.addClass('visible');
9931         }
9932         if (Roo.bootstrap.version == 3) {
9933             this.el.addClass(this.invalidClass);
9934         } else {
9935             this.inputEl().addClass('is-invalid');
9936         }
9937         
9938         
9939         
9940         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9941             
9942             var feedback = this.el.select('.form-control-feedback', true).first();
9943             
9944             if(feedback){
9945                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9946                 
9947                 if(this.getValue().length || this.forceFeedback){
9948                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9949                 }
9950                 
9951             }
9952             
9953         }
9954         
9955         this.fireEvent('invalid', this, msg);
9956     },
9957     // private
9958     SafariOnKeyDown : function(event)
9959     {
9960         // this is a workaround for a password hang bug on chrome/ webkit.
9961         if (this.inputEl().dom.type != 'password') {
9962             return;
9963         }
9964         
9965         var isSelectAll = false;
9966         
9967         if(this.inputEl().dom.selectionEnd > 0){
9968             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9969         }
9970         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9971             event.preventDefault();
9972             this.setValue('');
9973             return;
9974         }
9975         
9976         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9977             
9978             event.preventDefault();
9979             // this is very hacky as keydown always get's upper case.
9980             //
9981             var cc = String.fromCharCode(event.getCharCode());
9982             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9983             
9984         }
9985     },
9986     adjustWidth : function(tag, w){
9987         tag = tag.toLowerCase();
9988         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9989             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9990                 if(tag == 'input'){
9991                     return w + 2;
9992                 }
9993                 if(tag == 'textarea'){
9994                     return w-2;
9995                 }
9996             }else if(Roo.isOpera){
9997                 if(tag == 'input'){
9998                     return w + 2;
9999                 }
10000                 if(tag == 'textarea'){
10001                     return w-2;
10002                 }
10003             }
10004         }
10005         return w;
10006     },
10007     
10008     setFieldLabel : function(v)
10009     {
10010         if(!this.rendered){
10011             return;
10012         }
10013         
10014         if(this.indicatorEl()){
10015             var ar = this.el.select('label > span',true);
10016             
10017             if (ar.elements.length) {
10018                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10019                 this.fieldLabel = v;
10020                 return;
10021             }
10022             
10023             var br = this.el.select('label',true);
10024             
10025             if(br.elements.length) {
10026                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10027                 this.fieldLabel = v;
10028                 return;
10029             }
10030             
10031             Roo.log('Cannot Found any of label > span || label in input');
10032             return;
10033         }
10034         
10035         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10036         this.fieldLabel = v;
10037         
10038         
10039     }
10040 });
10041
10042  
10043 /*
10044  * - LGPL
10045  *
10046  * Input
10047  * 
10048  */
10049
10050 /**
10051  * @class Roo.bootstrap.TextArea
10052  * @extends Roo.bootstrap.Input
10053  * Bootstrap TextArea class
10054  * @cfg {Number} cols Specifies the visible width of a text area
10055  * @cfg {Number} rows Specifies the visible number of lines in a text area
10056  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10057  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10058  * @cfg {string} html text
10059  * 
10060  * @constructor
10061  * Create a new TextArea
10062  * @param {Object} config The config object
10063  */
10064
10065 Roo.bootstrap.TextArea = function(config){
10066     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10067    
10068 };
10069
10070 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10071      
10072     cols : false,
10073     rows : 5,
10074     readOnly : false,
10075     warp : 'soft',
10076     resize : false,
10077     value: false,
10078     html: false,
10079     
10080     getAutoCreate : function(){
10081         
10082         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10083         
10084         var id = Roo.id();
10085         
10086         var cfg = {};
10087         
10088         if(this.inputType != 'hidden'){
10089             cfg.cls = 'form-group' //input-group
10090         }
10091         
10092         var input =  {
10093             tag: 'textarea',
10094             id : id,
10095             warp : this.warp,
10096             rows : this.rows,
10097             value : this.value || '',
10098             html: this.html || '',
10099             cls : 'form-control',
10100             placeholder : this.placeholder || '' 
10101             
10102         };
10103         
10104         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10105             input.maxLength = this.maxLength;
10106         }
10107         
10108         if(this.resize){
10109             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10110         }
10111         
10112         if(this.cols){
10113             input.cols = this.cols;
10114         }
10115         
10116         if (this.readOnly) {
10117             input.readonly = true;
10118         }
10119         
10120         if (this.name) {
10121             input.name = this.name;
10122         }
10123         
10124         if (this.size) {
10125             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10126         }
10127         
10128         var settings=this;
10129         ['xs','sm','md','lg'].map(function(size){
10130             if (settings[size]) {
10131                 cfg.cls += ' col-' + size + '-' + settings[size];
10132             }
10133         });
10134         
10135         var inputblock = input;
10136         
10137         if(this.hasFeedback && !this.allowBlank){
10138             
10139             var feedback = {
10140                 tag: 'span',
10141                 cls: 'glyphicon form-control-feedback'
10142             };
10143
10144             inputblock = {
10145                 cls : 'has-feedback',
10146                 cn :  [
10147                     input,
10148                     feedback
10149                 ] 
10150             };  
10151         }
10152         
10153         
10154         if (this.before || this.after) {
10155             
10156             inputblock = {
10157                 cls : 'input-group',
10158                 cn :  [] 
10159             };
10160             if (this.before) {
10161                 inputblock.cn.push({
10162                     tag :'span',
10163                     cls : 'input-group-addon',
10164                     html : this.before
10165                 });
10166             }
10167             
10168             inputblock.cn.push(input);
10169             
10170             if(this.hasFeedback && !this.allowBlank){
10171                 inputblock.cls += ' has-feedback';
10172                 inputblock.cn.push(feedback);
10173             }
10174             
10175             if (this.after) {
10176                 inputblock.cn.push({
10177                     tag :'span',
10178                     cls : 'input-group-addon',
10179                     html : this.after
10180                 });
10181             }
10182             
10183         }
10184         
10185         if (align ==='left' && this.fieldLabel.length) {
10186             cfg.cn = [
10187                 {
10188                     tag: 'label',
10189                     'for' :  id,
10190                     cls : 'control-label',
10191                     html : this.fieldLabel
10192                 },
10193                 {
10194                     cls : "",
10195                     cn: [
10196                         inputblock
10197                     ]
10198                 }
10199
10200             ];
10201             
10202             if(this.labelWidth > 12){
10203                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10204             }
10205
10206             if(this.labelWidth < 13 && this.labelmd == 0){
10207                 this.labelmd = this.labelWidth;
10208             }
10209
10210             if(this.labellg > 0){
10211                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10212                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10213             }
10214
10215             if(this.labelmd > 0){
10216                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10217                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10218             }
10219
10220             if(this.labelsm > 0){
10221                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10222                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10223             }
10224
10225             if(this.labelxs > 0){
10226                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10227                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10228             }
10229             
10230         } else if ( this.fieldLabel.length) {
10231             cfg.cn = [
10232
10233                {
10234                    tag: 'label',
10235                    //cls : 'input-group-addon',
10236                    html : this.fieldLabel
10237
10238                },
10239
10240                inputblock
10241
10242            ];
10243
10244         } else {
10245
10246             cfg.cn = [
10247
10248                 inputblock
10249
10250             ];
10251                 
10252         }
10253         
10254         if (this.disabled) {
10255             input.disabled=true;
10256         }
10257         
10258         return cfg;
10259         
10260     },
10261     /**
10262      * return the real textarea element.
10263      */
10264     inputEl: function ()
10265     {
10266         return this.el.select('textarea.form-control',true).first();
10267     },
10268     
10269     /**
10270      * Clear any invalid styles/messages for this field
10271      */
10272     clearInvalid : function()
10273     {
10274         
10275         if(!this.el || this.preventMark){ // not rendered
10276             return;
10277         }
10278         
10279         var label = this.el.select('label', true).first();
10280         var icon = this.el.select('i.fa-star', true).first();
10281         
10282         if(label && icon){
10283             icon.remove();
10284         }
10285         this.el.removeClass( this.validClass);
10286         this.inputEl().removeClass('is-invalid');
10287          
10288         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10289             
10290             var feedback = this.el.select('.form-control-feedback', true).first();
10291             
10292             if(feedback){
10293                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10294             }
10295             
10296         }
10297         
10298         this.fireEvent('valid', this);
10299     },
10300     
10301      /**
10302      * Mark this field as valid
10303      */
10304     markValid : function()
10305     {
10306         if(!this.el  || this.preventMark){ // not rendered
10307             return;
10308         }
10309         
10310         this.el.removeClass([this.invalidClass, this.validClass]);
10311         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10312         
10313         var feedback = this.el.select('.form-control-feedback', true).first();
10314             
10315         if(feedback){
10316             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10317         }
10318
10319         if(this.disabled || this.allowBlank){
10320             return;
10321         }
10322         
10323         var label = this.el.select('label', true).first();
10324         var icon = this.el.select('i.fa-star', true).first();
10325         
10326         if(label && icon){
10327             icon.remove();
10328         }
10329         if (Roo.bootstrap.version == 3) {
10330             this.el.addClass(this.validClass);
10331         } else {
10332             this.inputEl().addClass('is-valid');
10333         }
10334         
10335         
10336         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10337             
10338             var feedback = this.el.select('.form-control-feedback', true).first();
10339             
10340             if(feedback){
10341                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10342                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10343             }
10344             
10345         }
10346         
10347         this.fireEvent('valid', this);
10348     },
10349     
10350      /**
10351      * Mark this field as invalid
10352      * @param {String} msg The validation message
10353      */
10354     markInvalid : function(msg)
10355     {
10356         if(!this.el  || this.preventMark){ // not rendered
10357             return;
10358         }
10359         
10360         this.el.removeClass([this.invalidClass, this.validClass]);
10361         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10362         
10363         var feedback = this.el.select('.form-control-feedback', true).first();
10364             
10365         if(feedback){
10366             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10367         }
10368
10369         if(this.disabled || this.allowBlank){
10370             return;
10371         }
10372         
10373         var label = this.el.select('label', true).first();
10374         var icon = this.el.select('i.fa-star', true).first();
10375         
10376         if(!this.getValue().length && label && !icon){
10377             this.el.createChild({
10378                 tag : 'i',
10379                 cls : 'text-danger fa fa-lg fa-star',
10380                 tooltip : 'This field is required',
10381                 style : 'margin-right:5px;'
10382             }, label, true);
10383         }
10384         
10385         if (Roo.bootstrap.version == 3) {
10386             this.el.addClass(this.invalidClass);
10387         } else {
10388             this.inputEl().addClass('is-invalid');
10389         }
10390         
10391         // fixme ... this may be depricated need to test..
10392         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10393             
10394             var feedback = this.el.select('.form-control-feedback', true).first();
10395             
10396             if(feedback){
10397                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10398                 
10399                 if(this.getValue().length || this.forceFeedback){
10400                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10401                 }
10402                 
10403             }
10404             
10405         }
10406         
10407         this.fireEvent('invalid', this, msg);
10408     }
10409 });
10410
10411  
10412 /*
10413  * - LGPL
10414  *
10415  * trigger field - base class for combo..
10416  * 
10417  */
10418  
10419 /**
10420  * @class Roo.bootstrap.TriggerField
10421  * @extends Roo.bootstrap.Input
10422  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10423  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10424  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10425  * for which you can provide a custom implementation.  For example:
10426  * <pre><code>
10427 var trigger = new Roo.bootstrap.TriggerField();
10428 trigger.onTriggerClick = myTriggerFn;
10429 trigger.applyTo('my-field');
10430 </code></pre>
10431  *
10432  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10433  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10434  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10435  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10436  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10437
10438  * @constructor
10439  * Create a new TriggerField.
10440  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10441  * to the base TextField)
10442  */
10443 Roo.bootstrap.TriggerField = function(config){
10444     this.mimicing = false;
10445     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10446 };
10447
10448 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10449     /**
10450      * @cfg {String} triggerClass A CSS class to apply to the trigger
10451      */
10452      /**
10453      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10454      */
10455     hideTrigger:false,
10456
10457     /**
10458      * @cfg {Boolean} removable (true|false) special filter default false
10459      */
10460     removable : false,
10461     
10462     /** @cfg {Boolean} grow @hide */
10463     /** @cfg {Number} growMin @hide */
10464     /** @cfg {Number} growMax @hide */
10465
10466     /**
10467      * @hide 
10468      * @method
10469      */
10470     autoSize: Roo.emptyFn,
10471     // private
10472     monitorTab : true,
10473     // private
10474     deferHeight : true,
10475
10476     
10477     actionMode : 'wrap',
10478     
10479     caret : false,
10480     
10481     
10482     getAutoCreate : function(){
10483        
10484         var align = this.labelAlign || this.parentLabelAlign();
10485         
10486         var id = Roo.id();
10487         
10488         var cfg = {
10489             cls: 'form-group' //input-group
10490         };
10491         
10492         
10493         var input =  {
10494             tag: 'input',
10495             id : id,
10496             type : this.inputType,
10497             cls : 'form-control',
10498             autocomplete: 'new-password',
10499             placeholder : this.placeholder || '' 
10500             
10501         };
10502         if (this.name) {
10503             input.name = this.name;
10504         }
10505         if (this.size) {
10506             input.cls += ' input-' + this.size;
10507         }
10508         
10509         if (this.disabled) {
10510             input.disabled=true;
10511         }
10512         
10513         var inputblock = input;
10514         
10515         if(this.hasFeedback && !this.allowBlank){
10516             
10517             var feedback = {
10518                 tag: 'span',
10519                 cls: 'glyphicon form-control-feedback'
10520             };
10521             
10522             if(this.removable && !this.editable && !this.tickable){
10523                 inputblock = {
10524                     cls : 'has-feedback',
10525                     cn :  [
10526                         inputblock,
10527                         {
10528                             tag: 'button',
10529                             html : 'x',
10530                             cls : 'roo-combo-removable-btn close'
10531                         },
10532                         feedback
10533                     ] 
10534                 };
10535             } else {
10536                 inputblock = {
10537                     cls : 'has-feedback',
10538                     cn :  [
10539                         inputblock,
10540                         feedback
10541                     ] 
10542                 };
10543             }
10544
10545         } else {
10546             if(this.removable && !this.editable && !this.tickable){
10547                 inputblock = {
10548                     cls : 'roo-removable',
10549                     cn :  [
10550                         inputblock,
10551                         {
10552                             tag: 'button',
10553                             html : 'x',
10554                             cls : 'roo-combo-removable-btn close'
10555                         }
10556                     ] 
10557                 };
10558             }
10559         }
10560         
10561         if (this.before || this.after) {
10562             
10563             inputblock = {
10564                 cls : 'input-group',
10565                 cn :  [] 
10566             };
10567             if (this.before) {
10568                 inputblock.cn.push({
10569                     tag :'span',
10570                     cls : 'input-group-addon input-group-prepend input-group-text',
10571                     html : this.before
10572                 });
10573             }
10574             
10575             inputblock.cn.push(input);
10576             
10577             if(this.hasFeedback && !this.allowBlank){
10578                 inputblock.cls += ' has-feedback';
10579                 inputblock.cn.push(feedback);
10580             }
10581             
10582             if (this.after) {
10583                 inputblock.cn.push({
10584                     tag :'span',
10585                     cls : 'input-group-addon input-group-append input-group-text',
10586                     html : this.after
10587                 });
10588             }
10589             
10590         };
10591         
10592       
10593         
10594         var ibwrap = inputblock;
10595         
10596         if(this.multiple){
10597             ibwrap = {
10598                 tag: 'ul',
10599                 cls: 'roo-select2-choices',
10600                 cn:[
10601                     {
10602                         tag: 'li',
10603                         cls: 'roo-select2-search-field',
10604                         cn: [
10605
10606                             inputblock
10607                         ]
10608                     }
10609                 ]
10610             };
10611                 
10612         }
10613         
10614         var combobox = {
10615             cls: 'roo-select2-container input-group',
10616             cn: [
10617                  {
10618                     tag: 'input',
10619                     type : 'hidden',
10620                     cls: 'form-hidden-field'
10621                 },
10622                 ibwrap
10623             ]
10624         };
10625         
10626         if(!this.multiple && this.showToggleBtn){
10627             
10628             var caret = {
10629                         tag: 'span',
10630                         cls: 'caret'
10631              };
10632             if (this.caret != false) {
10633                 caret = {
10634                      tag: 'i',
10635                      cls: 'fa fa-' + this.caret
10636                 };
10637                 
10638             }
10639             
10640             combobox.cn.push({
10641                 tag :'span',
10642                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10643                 cn : [
10644                     caret,
10645                     {
10646                         tag: 'span',
10647                         cls: 'combobox-clear',
10648                         cn  : [
10649                             {
10650                                 tag : 'i',
10651                                 cls: 'icon-remove'
10652                             }
10653                         ]
10654                     }
10655                 ]
10656
10657             })
10658         }
10659         
10660         if(this.multiple){
10661             combobox.cls += ' roo-select2-container-multi';
10662         }
10663          var indicator = {
10664             tag : 'i',
10665             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10666             tooltip : 'This field is required'
10667         };
10668         if (Roo.bootstrap.version == 4) {
10669             indicator = {
10670                 tag : 'i',
10671                 style : 'display:none'
10672             };
10673         }
10674         
10675         
10676         if (align ==='left' && this.fieldLabel.length) {
10677             
10678             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10679
10680             cfg.cn = [
10681                 indicator,
10682                 {
10683                     tag: 'label',
10684                     'for' :  id,
10685                     cls : 'control-label',
10686                     html : this.fieldLabel
10687
10688                 },
10689                 {
10690                     cls : "", 
10691                     cn: [
10692                         combobox
10693                     ]
10694                 }
10695
10696             ];
10697             
10698             var labelCfg = cfg.cn[1];
10699             var contentCfg = cfg.cn[2];
10700             
10701             if(this.indicatorpos == 'right'){
10702                 cfg.cn = [
10703                     {
10704                         tag: 'label',
10705                         'for' :  id,
10706                         cls : 'control-label',
10707                         cn : [
10708                             {
10709                                 tag : 'span',
10710                                 html : this.fieldLabel
10711                             },
10712                             indicator
10713                         ]
10714                     },
10715                     {
10716                         cls : "", 
10717                         cn: [
10718                             combobox
10719                         ]
10720                     }
10721
10722                 ];
10723                 
10724                 labelCfg = cfg.cn[0];
10725                 contentCfg = cfg.cn[1];
10726             }
10727             
10728             if(this.labelWidth > 12){
10729                 labelCfg.style = "width: " + this.labelWidth + 'px';
10730             }
10731             
10732             if(this.labelWidth < 13 && this.labelmd == 0){
10733                 this.labelmd = this.labelWidth;
10734             }
10735             
10736             if(this.labellg > 0){
10737                 labelCfg.cls += ' col-lg-' + this.labellg;
10738                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10739             }
10740             
10741             if(this.labelmd > 0){
10742                 labelCfg.cls += ' col-md-' + this.labelmd;
10743                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10744             }
10745             
10746             if(this.labelsm > 0){
10747                 labelCfg.cls += ' col-sm-' + this.labelsm;
10748                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10749             }
10750             
10751             if(this.labelxs > 0){
10752                 labelCfg.cls += ' col-xs-' + this.labelxs;
10753                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10754             }
10755             
10756         } else if ( this.fieldLabel.length) {
10757 //                Roo.log(" label");
10758             cfg.cn = [
10759                 indicator,
10760                {
10761                    tag: 'label',
10762                    //cls : 'input-group-addon',
10763                    html : this.fieldLabel
10764
10765                },
10766
10767                combobox
10768
10769             ];
10770             
10771             if(this.indicatorpos == 'right'){
10772                 
10773                 cfg.cn = [
10774                     {
10775                        tag: 'label',
10776                        cn : [
10777                            {
10778                                tag : 'span',
10779                                html : this.fieldLabel
10780                            },
10781                            indicator
10782                        ]
10783
10784                     },
10785                     combobox
10786
10787                 ];
10788
10789             }
10790
10791         } else {
10792             
10793 //                Roo.log(" no label && no align");
10794                 cfg = combobox
10795                      
10796                 
10797         }
10798         
10799         var settings=this;
10800         ['xs','sm','md','lg'].map(function(size){
10801             if (settings[size]) {
10802                 cfg.cls += ' col-' + size + '-' + settings[size];
10803             }
10804         });
10805         
10806         return cfg;
10807         
10808     },
10809     
10810     
10811     
10812     // private
10813     onResize : function(w, h){
10814 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10815 //        if(typeof w == 'number'){
10816 //            var x = w - this.trigger.getWidth();
10817 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10818 //            this.trigger.setStyle('left', x+'px');
10819 //        }
10820     },
10821
10822     // private
10823     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10824
10825     // private
10826     getResizeEl : function(){
10827         return this.inputEl();
10828     },
10829
10830     // private
10831     getPositionEl : function(){
10832         return this.inputEl();
10833     },
10834
10835     // private
10836     alignErrorIcon : function(){
10837         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10838     },
10839
10840     // private
10841     initEvents : function(){
10842         
10843         this.createList();
10844         
10845         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10846         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10847         if(!this.multiple && this.showToggleBtn){
10848             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10849             if(this.hideTrigger){
10850                 this.trigger.setDisplayed(false);
10851             }
10852             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10853         }
10854         
10855         if(this.multiple){
10856             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10857         }
10858         
10859         if(this.removable && !this.editable && !this.tickable){
10860             var close = this.closeTriggerEl();
10861             
10862             if(close){
10863                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10864                 close.on('click', this.removeBtnClick, this, close);
10865             }
10866         }
10867         
10868         //this.trigger.addClassOnOver('x-form-trigger-over');
10869         //this.trigger.addClassOnClick('x-form-trigger-click');
10870         
10871         //if(!this.width){
10872         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10873         //}
10874     },
10875     
10876     closeTriggerEl : function()
10877     {
10878         var close = this.el.select('.roo-combo-removable-btn', true).first();
10879         return close ? close : false;
10880     },
10881     
10882     removeBtnClick : function(e, h, el)
10883     {
10884         e.preventDefault();
10885         
10886         if(this.fireEvent("remove", this) !== false){
10887             this.reset();
10888             this.fireEvent("afterremove", this)
10889         }
10890     },
10891     
10892     createList : function()
10893     {
10894         this.list = Roo.get(document.body).createChild({
10895             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10896             cls: 'typeahead typeahead-long dropdown-menu',
10897             style: 'display:none'
10898         });
10899         
10900         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10901         
10902     },
10903
10904     // private
10905     initTrigger : function(){
10906        
10907     },
10908
10909     // private
10910     onDestroy : function(){
10911         if(this.trigger){
10912             this.trigger.removeAllListeners();
10913           //  this.trigger.remove();
10914         }
10915         //if(this.wrap){
10916         //    this.wrap.remove();
10917         //}
10918         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10919     },
10920
10921     // private
10922     onFocus : function(){
10923         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10924         /*
10925         if(!this.mimicing){
10926             this.wrap.addClass('x-trigger-wrap-focus');
10927             this.mimicing = true;
10928             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10929             if(this.monitorTab){
10930                 this.el.on("keydown", this.checkTab, this);
10931             }
10932         }
10933         */
10934     },
10935
10936     // private
10937     checkTab : function(e){
10938         if(e.getKey() == e.TAB){
10939             this.triggerBlur();
10940         }
10941     },
10942
10943     // private
10944     onBlur : function(){
10945         // do nothing
10946     },
10947
10948     // private
10949     mimicBlur : function(e, t){
10950         /*
10951         if(!this.wrap.contains(t) && this.validateBlur()){
10952             this.triggerBlur();
10953         }
10954         */
10955     },
10956
10957     // private
10958     triggerBlur : function(){
10959         this.mimicing = false;
10960         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10961         if(this.monitorTab){
10962             this.el.un("keydown", this.checkTab, this);
10963         }
10964         //this.wrap.removeClass('x-trigger-wrap-focus');
10965         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10966     },
10967
10968     // private
10969     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10970     validateBlur : function(e, t){
10971         return true;
10972     },
10973
10974     // private
10975     onDisable : function(){
10976         this.inputEl().dom.disabled = true;
10977         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10978         //if(this.wrap){
10979         //    this.wrap.addClass('x-item-disabled');
10980         //}
10981     },
10982
10983     // private
10984     onEnable : function(){
10985         this.inputEl().dom.disabled = false;
10986         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10987         //if(this.wrap){
10988         //    this.el.removeClass('x-item-disabled');
10989         //}
10990     },
10991
10992     // private
10993     onShow : function(){
10994         var ae = this.getActionEl();
10995         
10996         if(ae){
10997             ae.dom.style.display = '';
10998             ae.dom.style.visibility = 'visible';
10999         }
11000     },
11001
11002     // private
11003     
11004     onHide : function(){
11005         var ae = this.getActionEl();
11006         ae.dom.style.display = 'none';
11007     },
11008
11009     /**
11010      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
11011      * by an implementing function.
11012      * @method
11013      * @param {EventObject} e
11014      */
11015     onTriggerClick : Roo.emptyFn
11016 });
11017  /*
11018  * Based on:
11019  * Ext JS Library 1.1.1
11020  * Copyright(c) 2006-2007, Ext JS, LLC.
11021  *
11022  * Originally Released Under LGPL - original licence link has changed is not relivant.
11023  *
11024  * Fork - LGPL
11025  * <script type="text/javascript">
11026  */
11027
11028
11029 /**
11030  * @class Roo.data.SortTypes
11031  * @singleton
11032  * Defines the default sorting (casting?) comparison functions used when sorting data.
11033  */
11034 Roo.data.SortTypes = {
11035     /**
11036      * Default sort that does nothing
11037      * @param {Mixed} s The value being converted
11038      * @return {Mixed} The comparison value
11039      */
11040     none : function(s){
11041         return s;
11042     },
11043     
11044     /**
11045      * The regular expression used to strip tags
11046      * @type {RegExp}
11047      * @property
11048      */
11049     stripTagsRE : /<\/?[^>]+>/gi,
11050     
11051     /**
11052      * Strips all HTML tags to sort on text only
11053      * @param {Mixed} s The value being converted
11054      * @return {String} The comparison value
11055      */
11056     asText : function(s){
11057         return String(s).replace(this.stripTagsRE, "");
11058     },
11059     
11060     /**
11061      * Strips all HTML tags to sort on text only - Case insensitive
11062      * @param {Mixed} s The value being converted
11063      * @return {String} The comparison value
11064      */
11065     asUCText : function(s){
11066         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11067     },
11068     
11069     /**
11070      * Case insensitive string
11071      * @param {Mixed} s The value being converted
11072      * @return {String} The comparison value
11073      */
11074     asUCString : function(s) {
11075         return String(s).toUpperCase();
11076     },
11077     
11078     /**
11079      * Date sorting
11080      * @param {Mixed} s The value being converted
11081      * @return {Number} The comparison value
11082      */
11083     asDate : function(s) {
11084         if(!s){
11085             return 0;
11086         }
11087         if(s instanceof Date){
11088             return s.getTime();
11089         }
11090         return Date.parse(String(s));
11091     },
11092     
11093     /**
11094      * Float sorting
11095      * @param {Mixed} s The value being converted
11096      * @return {Float} The comparison value
11097      */
11098     asFloat : function(s) {
11099         var val = parseFloat(String(s).replace(/,/g, ""));
11100         if(isNaN(val)) {
11101             val = 0;
11102         }
11103         return val;
11104     },
11105     
11106     /**
11107      * Integer sorting
11108      * @param {Mixed} s The value being converted
11109      * @return {Number} The comparison value
11110      */
11111     asInt : function(s) {
11112         var val = parseInt(String(s).replace(/,/g, ""));
11113         if(isNaN(val)) {
11114             val = 0;
11115         }
11116         return val;
11117     }
11118 };/*
11119  * Based on:
11120  * Ext JS Library 1.1.1
11121  * Copyright(c) 2006-2007, Ext JS, LLC.
11122  *
11123  * Originally Released Under LGPL - original licence link has changed is not relivant.
11124  *
11125  * Fork - LGPL
11126  * <script type="text/javascript">
11127  */
11128
11129 /**
11130 * @class Roo.data.Record
11131  * Instances of this class encapsulate both record <em>definition</em> information, and record
11132  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11133  * to access Records cached in an {@link Roo.data.Store} object.<br>
11134  * <p>
11135  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11136  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11137  * objects.<br>
11138  * <p>
11139  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11140  * @constructor
11141  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11142  * {@link #create}. The parameters are the same.
11143  * @param {Array} data An associative Array of data values keyed by the field name.
11144  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11145  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11146  * not specified an integer id is generated.
11147  */
11148 Roo.data.Record = function(data, id){
11149     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11150     this.data = data;
11151 };
11152
11153 /**
11154  * Generate a constructor for a specific record layout.
11155  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11156  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11157  * Each field definition object may contain the following properties: <ul>
11158  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
11159  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11160  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11161  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11162  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11163  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11164  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11165  * this may be omitted.</p></li>
11166  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11167  * <ul><li>auto (Default, implies no conversion)</li>
11168  * <li>string</li>
11169  * <li>int</li>
11170  * <li>float</li>
11171  * <li>boolean</li>
11172  * <li>date</li></ul></p></li>
11173  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11174  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11175  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11176  * by the Reader into an object that will be stored in the Record. It is passed the
11177  * following parameters:<ul>
11178  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11179  * </ul></p></li>
11180  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11181  * </ul>
11182  * <br>usage:<br><pre><code>
11183 var TopicRecord = Roo.data.Record.create(
11184     {name: 'title', mapping: 'topic_title'},
11185     {name: 'author', mapping: 'username'},
11186     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11187     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11188     {name: 'lastPoster', mapping: 'user2'},
11189     {name: 'excerpt', mapping: 'post_text'}
11190 );
11191
11192 var myNewRecord = new TopicRecord({
11193     title: 'Do my job please',
11194     author: 'noobie',
11195     totalPosts: 1,
11196     lastPost: new Date(),
11197     lastPoster: 'Animal',
11198     excerpt: 'No way dude!'
11199 });
11200 myStore.add(myNewRecord);
11201 </code></pre>
11202  * @method create
11203  * @static
11204  */
11205 Roo.data.Record.create = function(o){
11206     var f = function(){
11207         f.superclass.constructor.apply(this, arguments);
11208     };
11209     Roo.extend(f, Roo.data.Record);
11210     var p = f.prototype;
11211     p.fields = new Roo.util.MixedCollection(false, function(field){
11212         return field.name;
11213     });
11214     for(var i = 0, len = o.length; i < len; i++){
11215         p.fields.add(new Roo.data.Field(o[i]));
11216     }
11217     f.getField = function(name){
11218         return p.fields.get(name);  
11219     };
11220     return f;
11221 };
11222
11223 Roo.data.Record.AUTO_ID = 1000;
11224 Roo.data.Record.EDIT = 'edit';
11225 Roo.data.Record.REJECT = 'reject';
11226 Roo.data.Record.COMMIT = 'commit';
11227
11228 Roo.data.Record.prototype = {
11229     /**
11230      * Readonly flag - true if this record has been modified.
11231      * @type Boolean
11232      */
11233     dirty : false,
11234     editing : false,
11235     error: null,
11236     modified: null,
11237
11238     // private
11239     join : function(store){
11240         this.store = store;
11241     },
11242
11243     /**
11244      * Set the named field to the specified value.
11245      * @param {String} name The name of the field to set.
11246      * @param {Object} value The value to set the field to.
11247      */
11248     set : function(name, value){
11249         if(this.data[name] == value){
11250             return;
11251         }
11252         this.dirty = true;
11253         if(!this.modified){
11254             this.modified = {};
11255         }
11256         if(typeof this.modified[name] == 'undefined'){
11257             this.modified[name] = this.data[name];
11258         }
11259         this.data[name] = value;
11260         if(!this.editing && this.store){
11261             this.store.afterEdit(this);
11262         }       
11263     },
11264
11265     /**
11266      * Get the value of the named field.
11267      * @param {String} name The name of the field to get the value of.
11268      * @return {Object} The value of the field.
11269      */
11270     get : function(name){
11271         return this.data[name]; 
11272     },
11273
11274     // private
11275     beginEdit : function(){
11276         this.editing = true;
11277         this.modified = {}; 
11278     },
11279
11280     // private
11281     cancelEdit : function(){
11282         this.editing = false;
11283         delete this.modified;
11284     },
11285
11286     // private
11287     endEdit : function(){
11288         this.editing = false;
11289         if(this.dirty && this.store){
11290             this.store.afterEdit(this);
11291         }
11292     },
11293
11294     /**
11295      * Usually called by the {@link Roo.data.Store} which owns the Record.
11296      * Rejects all changes made to the Record since either creation, or the last commit operation.
11297      * Modified fields are reverted to their original values.
11298      * <p>
11299      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11300      * of reject operations.
11301      */
11302     reject : function(){
11303         var m = this.modified;
11304         for(var n in m){
11305             if(typeof m[n] != "function"){
11306                 this.data[n] = m[n];
11307             }
11308         }
11309         this.dirty = false;
11310         delete this.modified;
11311         this.editing = false;
11312         if(this.store){
11313             this.store.afterReject(this);
11314         }
11315     },
11316
11317     /**
11318      * Usually called by the {@link Roo.data.Store} which owns the Record.
11319      * Commits all changes made to the Record since either creation, or the last commit operation.
11320      * <p>
11321      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11322      * of commit operations.
11323      */
11324     commit : function(){
11325         this.dirty = false;
11326         delete this.modified;
11327         this.editing = false;
11328         if(this.store){
11329             this.store.afterCommit(this);
11330         }
11331     },
11332
11333     // private
11334     hasError : function(){
11335         return this.error != null;
11336     },
11337
11338     // private
11339     clearError : function(){
11340         this.error = null;
11341     },
11342
11343     /**
11344      * Creates a copy of this record.
11345      * @param {String} id (optional) A new record id if you don't want to use this record's id
11346      * @return {Record}
11347      */
11348     copy : function(newId) {
11349         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11350     }
11351 };/*
11352  * Based on:
11353  * Ext JS Library 1.1.1
11354  * Copyright(c) 2006-2007, Ext JS, LLC.
11355  *
11356  * Originally Released Under LGPL - original licence link has changed is not relivant.
11357  *
11358  * Fork - LGPL
11359  * <script type="text/javascript">
11360  */
11361
11362
11363
11364 /**
11365  * @class Roo.data.Store
11366  * @extends Roo.util.Observable
11367  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11368  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11369  * <p>
11370  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
11371  * has no knowledge of the format of the data returned by the Proxy.<br>
11372  * <p>
11373  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11374  * instances from the data object. These records are cached and made available through accessor functions.
11375  * @constructor
11376  * Creates a new Store.
11377  * @param {Object} config A config object containing the objects needed for the Store to access data,
11378  * and read the data into Records.
11379  */
11380 Roo.data.Store = function(config){
11381     this.data = new Roo.util.MixedCollection(false);
11382     this.data.getKey = function(o){
11383         return o.id;
11384     };
11385     this.baseParams = {};
11386     // private
11387     this.paramNames = {
11388         "start" : "start",
11389         "limit" : "limit",
11390         "sort" : "sort",
11391         "dir" : "dir",
11392         "multisort" : "_multisort"
11393     };
11394
11395     if(config && config.data){
11396         this.inlineData = config.data;
11397         delete config.data;
11398     }
11399
11400     Roo.apply(this, config);
11401     
11402     if(this.reader){ // reader passed
11403         this.reader = Roo.factory(this.reader, Roo.data);
11404         this.reader.xmodule = this.xmodule || false;
11405         if(!this.recordType){
11406             this.recordType = this.reader.recordType;
11407         }
11408         if(this.reader.onMetaChange){
11409             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11410         }
11411     }
11412
11413     if(this.recordType){
11414         this.fields = this.recordType.prototype.fields;
11415     }
11416     this.modified = [];
11417
11418     this.addEvents({
11419         /**
11420          * @event datachanged
11421          * Fires when the data cache has changed, and a widget which is using this Store
11422          * as a Record cache should refresh its view.
11423          * @param {Store} this
11424          */
11425         datachanged : true,
11426         /**
11427          * @event metachange
11428          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11429          * @param {Store} this
11430          * @param {Object} meta The JSON metadata
11431          */
11432         metachange : true,
11433         /**
11434          * @event add
11435          * Fires when Records have been added to the Store
11436          * @param {Store} this
11437          * @param {Roo.data.Record[]} records The array of Records added
11438          * @param {Number} index The index at which the record(s) were added
11439          */
11440         add : true,
11441         /**
11442          * @event remove
11443          * Fires when a Record has been removed from the Store
11444          * @param {Store} this
11445          * @param {Roo.data.Record} record The Record that was removed
11446          * @param {Number} index The index at which the record was removed
11447          */
11448         remove : true,
11449         /**
11450          * @event update
11451          * Fires when a Record has been updated
11452          * @param {Store} this
11453          * @param {Roo.data.Record} record The Record that was updated
11454          * @param {String} operation The update operation being performed.  Value may be one of:
11455          * <pre><code>
11456  Roo.data.Record.EDIT
11457  Roo.data.Record.REJECT
11458  Roo.data.Record.COMMIT
11459          * </code></pre>
11460          */
11461         update : true,
11462         /**
11463          * @event clear
11464          * Fires when the data cache has been cleared.
11465          * @param {Store} this
11466          */
11467         clear : true,
11468         /**
11469          * @event beforeload
11470          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11471          * the load action will be canceled.
11472          * @param {Store} this
11473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11474          */
11475         beforeload : true,
11476         /**
11477          * @event beforeloadadd
11478          * Fires after a new set of Records has been loaded.
11479          * @param {Store} this
11480          * @param {Roo.data.Record[]} records The Records that were loaded
11481          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11482          */
11483         beforeloadadd : true,
11484         /**
11485          * @event load
11486          * Fires after a new set of Records has been loaded, before they are added to the store.
11487          * @param {Store} this
11488          * @param {Roo.data.Record[]} records The Records that were loaded
11489          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11490          * @params {Object} return from reader
11491          */
11492         load : true,
11493         /**
11494          * @event loadexception
11495          * Fires if an exception occurs in the Proxy during loading.
11496          * Called with the signature of the Proxy's "loadexception" event.
11497          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11498          * 
11499          * @param {Proxy} 
11500          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11501          * @param {Object} load options 
11502          * @param {Object} jsonData from your request (normally this contains the Exception)
11503          */
11504         loadexception : true
11505     });
11506     
11507     if(this.proxy){
11508         this.proxy = Roo.factory(this.proxy, Roo.data);
11509         this.proxy.xmodule = this.xmodule || false;
11510         this.relayEvents(this.proxy,  ["loadexception"]);
11511     }
11512     this.sortToggle = {};
11513     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11514
11515     Roo.data.Store.superclass.constructor.call(this);
11516
11517     if(this.inlineData){
11518         this.loadData(this.inlineData);
11519         delete this.inlineData;
11520     }
11521 };
11522
11523 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11524      /**
11525     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11526     * without a remote query - used by combo/forms at present.
11527     */
11528     
11529     /**
11530     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11531     */
11532     /**
11533     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11534     */
11535     /**
11536     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11537     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11538     */
11539     /**
11540     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11541     * on any HTTP request
11542     */
11543     /**
11544     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11545     */
11546     /**
11547     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11548     */
11549     multiSort: false,
11550     /**
11551     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11552     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11553     */
11554     remoteSort : false,
11555
11556     /**
11557     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11558      * loaded or when a record is removed. (defaults to false).
11559     */
11560     pruneModifiedRecords : false,
11561
11562     // private
11563     lastOptions : null,
11564
11565     /**
11566      * Add Records to the Store and fires the add event.
11567      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11568      */
11569     add : function(records){
11570         records = [].concat(records);
11571         for(var i = 0, len = records.length; i < len; i++){
11572             records[i].join(this);
11573         }
11574         var index = this.data.length;
11575         this.data.addAll(records);
11576         this.fireEvent("add", this, records, index);
11577     },
11578
11579     /**
11580      * Remove a Record from the Store and fires the remove event.
11581      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11582      */
11583     remove : function(record){
11584         var index = this.data.indexOf(record);
11585         this.data.removeAt(index);
11586  
11587         if(this.pruneModifiedRecords){
11588             this.modified.remove(record);
11589         }
11590         this.fireEvent("remove", this, record, index);
11591     },
11592
11593     /**
11594      * Remove all Records from the Store and fires the clear event.
11595      */
11596     removeAll : function(){
11597         this.data.clear();
11598         if(this.pruneModifiedRecords){
11599             this.modified = [];
11600         }
11601         this.fireEvent("clear", this);
11602     },
11603
11604     /**
11605      * Inserts Records to the Store at the given index and fires the add event.
11606      * @param {Number} index The start index at which to insert the passed Records.
11607      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11608      */
11609     insert : function(index, records){
11610         records = [].concat(records);
11611         for(var i = 0, len = records.length; i < len; i++){
11612             this.data.insert(index, records[i]);
11613             records[i].join(this);
11614         }
11615         this.fireEvent("add", this, records, index);
11616     },
11617
11618     /**
11619      * Get the index within the cache of the passed Record.
11620      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11621      * @return {Number} The index of the passed Record. Returns -1 if not found.
11622      */
11623     indexOf : function(record){
11624         return this.data.indexOf(record);
11625     },
11626
11627     /**
11628      * Get the index within the cache of the Record with the passed id.
11629      * @param {String} id The id of the Record to find.
11630      * @return {Number} The index of the Record. Returns -1 if not found.
11631      */
11632     indexOfId : function(id){
11633         return this.data.indexOfKey(id);
11634     },
11635
11636     /**
11637      * Get the Record with the specified id.
11638      * @param {String} id The id of the Record to find.
11639      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11640      */
11641     getById : function(id){
11642         return this.data.key(id);
11643     },
11644
11645     /**
11646      * Get the Record at the specified index.
11647      * @param {Number} index The index of the Record to find.
11648      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11649      */
11650     getAt : function(index){
11651         return this.data.itemAt(index);
11652     },
11653
11654     /**
11655      * Returns a range of Records between specified indices.
11656      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11657      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11658      * @return {Roo.data.Record[]} An array of Records
11659      */
11660     getRange : function(start, end){
11661         return this.data.getRange(start, end);
11662     },
11663
11664     // private
11665     storeOptions : function(o){
11666         o = Roo.apply({}, o);
11667         delete o.callback;
11668         delete o.scope;
11669         this.lastOptions = o;
11670     },
11671
11672     /**
11673      * Loads the Record cache from the configured Proxy using the configured Reader.
11674      * <p>
11675      * If using remote paging, then the first load call must specify the <em>start</em>
11676      * and <em>limit</em> properties in the options.params property to establish the initial
11677      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11678      * <p>
11679      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11680      * and this call will return before the new data has been loaded. Perform any post-processing
11681      * in a callback function, or in a "load" event handler.</strong>
11682      * <p>
11683      * @param {Object} options An object containing properties which control loading options:<ul>
11684      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11685      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11686      * passed the following arguments:<ul>
11687      * <li>r : Roo.data.Record[]</li>
11688      * <li>options: Options object from the load call</li>
11689      * <li>success: Boolean success indicator</li></ul></li>
11690      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11691      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11692      * </ul>
11693      */
11694     load : function(options){
11695         options = options || {};
11696         if(this.fireEvent("beforeload", this, options) !== false){
11697             this.storeOptions(options);
11698             var p = Roo.apply(options.params || {}, this.baseParams);
11699             // if meta was not loaded from remote source.. try requesting it.
11700             if (!this.reader.metaFromRemote) {
11701                 p._requestMeta = 1;
11702             }
11703             if(this.sortInfo && this.remoteSort){
11704                 var pn = this.paramNames;
11705                 p[pn["sort"]] = this.sortInfo.field;
11706                 p[pn["dir"]] = this.sortInfo.direction;
11707             }
11708             if (this.multiSort) {
11709                 var pn = this.paramNames;
11710                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11711             }
11712             
11713             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11714         }
11715     },
11716
11717     /**
11718      * Reloads the Record cache from the configured Proxy using the configured Reader and
11719      * the options from the last load operation performed.
11720      * @param {Object} options (optional) An object containing properties which may override the options
11721      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11722      * the most recently used options are reused).
11723      */
11724     reload : function(options){
11725         this.load(Roo.applyIf(options||{}, this.lastOptions));
11726     },
11727
11728     // private
11729     // Called as a callback by the Reader during a load operation.
11730     loadRecords : function(o, options, success){
11731         if(!o || success === false){
11732             if(success !== false){
11733                 this.fireEvent("load", this, [], options, o);
11734             }
11735             if(options.callback){
11736                 options.callback.call(options.scope || this, [], options, false);
11737             }
11738             return;
11739         }
11740         // if data returned failure - throw an exception.
11741         if (o.success === false) {
11742             // show a message if no listener is registered.
11743             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11744                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11745             }
11746             // loadmask wil be hooked into this..
11747             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11748             return;
11749         }
11750         var r = o.records, t = o.totalRecords || r.length;
11751         
11752         this.fireEvent("beforeloadadd", this, r, options, o);
11753         
11754         if(!options || options.add !== true){
11755             if(this.pruneModifiedRecords){
11756                 this.modified = [];
11757             }
11758             for(var i = 0, len = r.length; i < len; i++){
11759                 r[i].join(this);
11760             }
11761             if(this.snapshot){
11762                 this.data = this.snapshot;
11763                 delete this.snapshot;
11764             }
11765             this.data.clear();
11766             this.data.addAll(r);
11767             this.totalLength = t;
11768             this.applySort();
11769             this.fireEvent("datachanged", this);
11770         }else{
11771             this.totalLength = Math.max(t, this.data.length+r.length);
11772             this.add(r);
11773         }
11774         
11775         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11776                 
11777             var e = new Roo.data.Record({});
11778
11779             e.set(this.parent.displayField, this.parent.emptyTitle);
11780             e.set(this.parent.valueField, '');
11781
11782             this.insert(0, e);
11783         }
11784             
11785         this.fireEvent("load", this, r, options, o);
11786         if(options.callback){
11787             options.callback.call(options.scope || this, r, options, true);
11788         }
11789     },
11790
11791
11792     /**
11793      * Loads data from a passed data block. A Reader which understands the format of the data
11794      * must have been configured in the constructor.
11795      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11796      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11797      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11798      */
11799     loadData : function(o, append){
11800         var r = this.reader.readRecords(o);
11801         this.loadRecords(r, {add: append}, true);
11802     },
11803
11804     /**
11805      * Gets the number of cached records.
11806      * <p>
11807      * <em>If using paging, this may not be the total size of the dataset. If the data object
11808      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11809      * the data set size</em>
11810      */
11811     getCount : function(){
11812         return this.data.length || 0;
11813     },
11814
11815     /**
11816      * Gets the total number of records in the dataset as returned by the server.
11817      * <p>
11818      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11819      * the dataset size</em>
11820      */
11821     getTotalCount : function(){
11822         return this.totalLength || 0;
11823     },
11824
11825     /**
11826      * Returns the sort state of the Store as an object with two properties:
11827      * <pre><code>
11828  field {String} The name of the field by which the Records are sorted
11829  direction {String} The sort order, "ASC" or "DESC"
11830      * </code></pre>
11831      */
11832     getSortState : function(){
11833         return this.sortInfo;
11834     },
11835
11836     // private
11837     applySort : function(){
11838         if(this.sortInfo && !this.remoteSort){
11839             var s = this.sortInfo, f = s.field;
11840             var st = this.fields.get(f).sortType;
11841             var fn = function(r1, r2){
11842                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11843                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11844             };
11845             this.data.sort(s.direction, fn);
11846             if(this.snapshot && this.snapshot != this.data){
11847                 this.snapshot.sort(s.direction, fn);
11848             }
11849         }
11850     },
11851
11852     /**
11853      * Sets the default sort column and order to be used by the next load operation.
11854      * @param {String} fieldName The name of the field to sort by.
11855      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11856      */
11857     setDefaultSort : function(field, dir){
11858         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11859     },
11860
11861     /**
11862      * Sort the Records.
11863      * If remote sorting is used, the sort is performed on the server, and the cache is
11864      * reloaded. If local sorting is used, the cache is sorted internally.
11865      * @param {String} fieldName The name of the field to sort by.
11866      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11867      */
11868     sort : function(fieldName, dir){
11869         var f = this.fields.get(fieldName);
11870         if(!dir){
11871             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11872             
11873             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11874                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11875             }else{
11876                 dir = f.sortDir;
11877             }
11878         }
11879         this.sortToggle[f.name] = dir;
11880         this.sortInfo = {field: f.name, direction: dir};
11881         if(!this.remoteSort){
11882             this.applySort();
11883             this.fireEvent("datachanged", this);
11884         }else{
11885             this.load(this.lastOptions);
11886         }
11887     },
11888
11889     /**
11890      * Calls the specified function for each of the Records in the cache.
11891      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11892      * Returning <em>false</em> aborts and exits the iteration.
11893      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11894      */
11895     each : function(fn, scope){
11896         this.data.each(fn, scope);
11897     },
11898
11899     /**
11900      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11901      * (e.g., during paging).
11902      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11903      */
11904     getModifiedRecords : function(){
11905         return this.modified;
11906     },
11907
11908     // private
11909     createFilterFn : function(property, value, anyMatch){
11910         if(!value.exec){ // not a regex
11911             value = String(value);
11912             if(value.length == 0){
11913                 return false;
11914             }
11915             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11916         }
11917         return function(r){
11918             return value.test(r.data[property]);
11919         };
11920     },
11921
11922     /**
11923      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11924      * @param {String} property A field on your records
11925      * @param {Number} start The record index to start at (defaults to 0)
11926      * @param {Number} end The last record index to include (defaults to length - 1)
11927      * @return {Number} The sum
11928      */
11929     sum : function(property, start, end){
11930         var rs = this.data.items, v = 0;
11931         start = start || 0;
11932         end = (end || end === 0) ? end : rs.length-1;
11933
11934         for(var i = start; i <= end; i++){
11935             v += (rs[i].data[property] || 0);
11936         }
11937         return v;
11938     },
11939
11940     /**
11941      * Filter the records by a specified property.
11942      * @param {String} field A field on your records
11943      * @param {String/RegExp} value Either a string that the field
11944      * should start with or a RegExp to test against the field
11945      * @param {Boolean} anyMatch True to match any part not just the beginning
11946      */
11947     filter : function(property, value, anyMatch){
11948         var fn = this.createFilterFn(property, value, anyMatch);
11949         return fn ? this.filterBy(fn) : this.clearFilter();
11950     },
11951
11952     /**
11953      * Filter by a function. The specified function will be called with each
11954      * record in this data source. If the function returns true the record is included,
11955      * otherwise it is filtered.
11956      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11957      * @param {Object} scope (optional) The scope of the function (defaults to this)
11958      */
11959     filterBy : function(fn, scope){
11960         this.snapshot = this.snapshot || this.data;
11961         this.data = this.queryBy(fn, scope||this);
11962         this.fireEvent("datachanged", this);
11963     },
11964
11965     /**
11966      * Query the records by a specified property.
11967      * @param {String} field A field on your records
11968      * @param {String/RegExp} value Either a string that the field
11969      * should start with or a RegExp to test against the field
11970      * @param {Boolean} anyMatch True to match any part not just the beginning
11971      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11972      */
11973     query : function(property, value, anyMatch){
11974         var fn = this.createFilterFn(property, value, anyMatch);
11975         return fn ? this.queryBy(fn) : this.data.clone();
11976     },
11977
11978     /**
11979      * Query by a function. The specified function will be called with each
11980      * record in this data source. If the function returns true the record is included
11981      * in the results.
11982      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11983      * @param {Object} scope (optional) The scope of the function (defaults to this)
11984       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11985      **/
11986     queryBy : function(fn, scope){
11987         var data = this.snapshot || this.data;
11988         return data.filterBy(fn, scope||this);
11989     },
11990
11991     /**
11992      * Collects unique values for a particular dataIndex from this store.
11993      * @param {String} dataIndex The property to collect
11994      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11995      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11996      * @return {Array} An array of the unique values
11997      **/
11998     collect : function(dataIndex, allowNull, bypassFilter){
11999         var d = (bypassFilter === true && this.snapshot) ?
12000                 this.snapshot.items : this.data.items;
12001         var v, sv, r = [], l = {};
12002         for(var i = 0, len = d.length; i < len; i++){
12003             v = d[i].data[dataIndex];
12004             sv = String(v);
12005             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
12006                 l[sv] = true;
12007                 r[r.length] = v;
12008             }
12009         }
12010         return r;
12011     },
12012
12013     /**
12014      * Revert to a view of the Record cache with no filtering applied.
12015      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
12016      */
12017     clearFilter : function(suppressEvent){
12018         if(this.snapshot && this.snapshot != this.data){
12019             this.data = this.snapshot;
12020             delete this.snapshot;
12021             if(suppressEvent !== true){
12022                 this.fireEvent("datachanged", this);
12023             }
12024         }
12025     },
12026
12027     // private
12028     afterEdit : function(record){
12029         if(this.modified.indexOf(record) == -1){
12030             this.modified.push(record);
12031         }
12032         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
12033     },
12034     
12035     // private
12036     afterReject : function(record){
12037         this.modified.remove(record);
12038         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12039     },
12040
12041     // private
12042     afterCommit : function(record){
12043         this.modified.remove(record);
12044         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12045     },
12046
12047     /**
12048      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12049      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12050      */
12051     commitChanges : function(){
12052         var m = this.modified.slice(0);
12053         this.modified = [];
12054         for(var i = 0, len = m.length; i < len; i++){
12055             m[i].commit();
12056         }
12057     },
12058
12059     /**
12060      * Cancel outstanding changes on all changed records.
12061      */
12062     rejectChanges : function(){
12063         var m = this.modified.slice(0);
12064         this.modified = [];
12065         for(var i = 0, len = m.length; i < len; i++){
12066             m[i].reject();
12067         }
12068     },
12069
12070     onMetaChange : function(meta, rtype, o){
12071         this.recordType = rtype;
12072         this.fields = rtype.prototype.fields;
12073         delete this.snapshot;
12074         this.sortInfo = meta.sortInfo || this.sortInfo;
12075         this.modified = [];
12076         this.fireEvent('metachange', this, this.reader.meta);
12077     },
12078     
12079     moveIndex : function(data, type)
12080     {
12081         var index = this.indexOf(data);
12082         
12083         var newIndex = index + type;
12084         
12085         this.remove(data);
12086         
12087         this.insert(newIndex, data);
12088         
12089     }
12090 });/*
12091  * Based on:
12092  * Ext JS Library 1.1.1
12093  * Copyright(c) 2006-2007, Ext JS, LLC.
12094  *
12095  * Originally Released Under LGPL - original licence link has changed is not relivant.
12096  *
12097  * Fork - LGPL
12098  * <script type="text/javascript">
12099  */
12100
12101 /**
12102  * @class Roo.data.SimpleStore
12103  * @extends Roo.data.Store
12104  * Small helper class to make creating Stores from Array data easier.
12105  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12106  * @cfg {Array} fields An array of field definition objects, or field name strings.
12107  * @cfg {Array} data The multi-dimensional array of data
12108  * @constructor
12109  * @param {Object} config
12110  */
12111 Roo.data.SimpleStore = function(config){
12112     Roo.data.SimpleStore.superclass.constructor.call(this, {
12113         isLocal : true,
12114         reader: new Roo.data.ArrayReader({
12115                 id: config.id
12116             },
12117             Roo.data.Record.create(config.fields)
12118         ),
12119         proxy : new Roo.data.MemoryProxy(config.data)
12120     });
12121     this.load();
12122 };
12123 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12124  * Based on:
12125  * Ext JS Library 1.1.1
12126  * Copyright(c) 2006-2007, Ext JS, LLC.
12127  *
12128  * Originally Released Under LGPL - original licence link has changed is not relivant.
12129  *
12130  * Fork - LGPL
12131  * <script type="text/javascript">
12132  */
12133
12134 /**
12135 /**
12136  * @extends Roo.data.Store
12137  * @class Roo.data.JsonStore
12138  * Small helper class to make creating Stores for JSON data easier. <br/>
12139 <pre><code>
12140 var store = new Roo.data.JsonStore({
12141     url: 'get-images.php',
12142     root: 'images',
12143     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12144 });
12145 </code></pre>
12146  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12147  * JsonReader and HttpProxy (unless inline data is provided).</b>
12148  * @cfg {Array} fields An array of field definition objects, or field name strings.
12149  * @constructor
12150  * @param {Object} config
12151  */
12152 Roo.data.JsonStore = function(c){
12153     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12154         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12155         reader: new Roo.data.JsonReader(c, c.fields)
12156     }));
12157 };
12158 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12159  * Based on:
12160  * Ext JS Library 1.1.1
12161  * Copyright(c) 2006-2007, Ext JS, LLC.
12162  *
12163  * Originally Released Under LGPL - original licence link has changed is not relivant.
12164  *
12165  * Fork - LGPL
12166  * <script type="text/javascript">
12167  */
12168
12169  
12170 Roo.data.Field = function(config){
12171     if(typeof config == "string"){
12172         config = {name: config};
12173     }
12174     Roo.apply(this, config);
12175     
12176     if(!this.type){
12177         this.type = "auto";
12178     }
12179     
12180     var st = Roo.data.SortTypes;
12181     // named sortTypes are supported, here we look them up
12182     if(typeof this.sortType == "string"){
12183         this.sortType = st[this.sortType];
12184     }
12185     
12186     // set default sortType for strings and dates
12187     if(!this.sortType){
12188         switch(this.type){
12189             case "string":
12190                 this.sortType = st.asUCString;
12191                 break;
12192             case "date":
12193                 this.sortType = st.asDate;
12194                 break;
12195             default:
12196                 this.sortType = st.none;
12197         }
12198     }
12199
12200     // define once
12201     var stripRe = /[\$,%]/g;
12202
12203     // prebuilt conversion function for this field, instead of
12204     // switching every time we're reading a value
12205     if(!this.convert){
12206         var cv, dateFormat = this.dateFormat;
12207         switch(this.type){
12208             case "":
12209             case "auto":
12210             case undefined:
12211                 cv = function(v){ return v; };
12212                 break;
12213             case "string":
12214                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12215                 break;
12216             case "int":
12217                 cv = function(v){
12218                     return v !== undefined && v !== null && v !== '' ?
12219                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12220                     };
12221                 break;
12222             case "float":
12223                 cv = function(v){
12224                     return v !== undefined && v !== null && v !== '' ?
12225                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12226                     };
12227                 break;
12228             case "bool":
12229             case "boolean":
12230                 cv = function(v){ return v === true || v === "true" || v == 1; };
12231                 break;
12232             case "date":
12233                 cv = function(v){
12234                     if(!v){
12235                         return '';
12236                     }
12237                     if(v instanceof Date){
12238                         return v;
12239                     }
12240                     if(dateFormat){
12241                         if(dateFormat == "timestamp"){
12242                             return new Date(v*1000);
12243                         }
12244                         return Date.parseDate(v, dateFormat);
12245                     }
12246                     var parsed = Date.parse(v);
12247                     return parsed ? new Date(parsed) : null;
12248                 };
12249              break;
12250             
12251         }
12252         this.convert = cv;
12253     }
12254 };
12255
12256 Roo.data.Field.prototype = {
12257     dateFormat: null,
12258     defaultValue: "",
12259     mapping: null,
12260     sortType : null,
12261     sortDir : "ASC"
12262 };/*
12263  * Based on:
12264  * Ext JS Library 1.1.1
12265  * Copyright(c) 2006-2007, Ext JS, LLC.
12266  *
12267  * Originally Released Under LGPL - original licence link has changed is not relivant.
12268  *
12269  * Fork - LGPL
12270  * <script type="text/javascript">
12271  */
12272  
12273 // Base class for reading structured data from a data source.  This class is intended to be
12274 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12275
12276 /**
12277  * @class Roo.data.DataReader
12278  * Base class for reading structured data from a data source.  This class is intended to be
12279  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12280  */
12281
12282 Roo.data.DataReader = function(meta, recordType){
12283     
12284     this.meta = meta;
12285     
12286     this.recordType = recordType instanceof Array ? 
12287         Roo.data.Record.create(recordType) : recordType;
12288 };
12289
12290 Roo.data.DataReader.prototype = {
12291      /**
12292      * Create an empty record
12293      * @param {Object} data (optional) - overlay some values
12294      * @return {Roo.data.Record} record created.
12295      */
12296     newRow :  function(d) {
12297         var da =  {};
12298         this.recordType.prototype.fields.each(function(c) {
12299             switch( c.type) {
12300                 case 'int' : da[c.name] = 0; break;
12301                 case 'date' : da[c.name] = new Date(); break;
12302                 case 'float' : da[c.name] = 0.0; break;
12303                 case 'boolean' : da[c.name] = false; break;
12304                 default : da[c.name] = ""; break;
12305             }
12306             
12307         });
12308         return new this.recordType(Roo.apply(da, d));
12309     }
12310     
12311 };/*
12312  * Based on:
12313  * Ext JS Library 1.1.1
12314  * Copyright(c) 2006-2007, Ext JS, LLC.
12315  *
12316  * Originally Released Under LGPL - original licence link has changed is not relivant.
12317  *
12318  * Fork - LGPL
12319  * <script type="text/javascript">
12320  */
12321
12322 /**
12323  * @class Roo.data.DataProxy
12324  * @extends Roo.data.Observable
12325  * This class is an abstract base class for implementations which provide retrieval of
12326  * unformatted data objects.<br>
12327  * <p>
12328  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12329  * (of the appropriate type which knows how to parse the data object) to provide a block of
12330  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12331  * <p>
12332  * Custom implementations must implement the load method as described in
12333  * {@link Roo.data.HttpProxy#load}.
12334  */
12335 Roo.data.DataProxy = function(){
12336     this.addEvents({
12337         /**
12338          * @event beforeload
12339          * Fires before a network request is made to retrieve a data object.
12340          * @param {Object} This DataProxy object.
12341          * @param {Object} params The params parameter to the load function.
12342          */
12343         beforeload : true,
12344         /**
12345          * @event load
12346          * Fires before the load method's callback is called.
12347          * @param {Object} This DataProxy object.
12348          * @param {Object} o The data object.
12349          * @param {Object} arg The callback argument object passed to the load function.
12350          */
12351         load : true,
12352         /**
12353          * @event loadexception
12354          * Fires if an Exception occurs during data retrieval.
12355          * @param {Object} This DataProxy object.
12356          * @param {Object} o The data object.
12357          * @param {Object} arg The callback argument object passed to the load function.
12358          * @param {Object} e The Exception.
12359          */
12360         loadexception : true
12361     });
12362     Roo.data.DataProxy.superclass.constructor.call(this);
12363 };
12364
12365 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12366
12367     /**
12368      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12369      */
12370 /*
12371  * Based on:
12372  * Ext JS Library 1.1.1
12373  * Copyright(c) 2006-2007, Ext JS, LLC.
12374  *
12375  * Originally Released Under LGPL - original licence link has changed is not relivant.
12376  *
12377  * Fork - LGPL
12378  * <script type="text/javascript">
12379  */
12380 /**
12381  * @class Roo.data.MemoryProxy
12382  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12383  * to the Reader when its load method is called.
12384  * @constructor
12385  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12386  */
12387 Roo.data.MemoryProxy = function(data){
12388     if (data.data) {
12389         data = data.data;
12390     }
12391     Roo.data.MemoryProxy.superclass.constructor.call(this);
12392     this.data = data;
12393 };
12394
12395 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12396     
12397     /**
12398      * Load data from the requested source (in this case an in-memory
12399      * data object passed to the constructor), read the data object into
12400      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12401      * process that block using the passed callback.
12402      * @param {Object} params This parameter is not used by the MemoryProxy class.
12403      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12404      * object into a block of Roo.data.Records.
12405      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12406      * The function must be passed <ul>
12407      * <li>The Record block object</li>
12408      * <li>The "arg" argument from the load function</li>
12409      * <li>A boolean success indicator</li>
12410      * </ul>
12411      * @param {Object} scope The scope in which to call the callback
12412      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12413      */
12414     load : function(params, reader, callback, scope, arg){
12415         params = params || {};
12416         var result;
12417         try {
12418             result = reader.readRecords(params.data ? params.data :this.data);
12419         }catch(e){
12420             this.fireEvent("loadexception", this, arg, null, e);
12421             callback.call(scope, null, arg, false);
12422             return;
12423         }
12424         callback.call(scope, result, arg, true);
12425     },
12426     
12427     // private
12428     update : function(params, records){
12429         
12430     }
12431 });/*
12432  * Based on:
12433  * Ext JS Library 1.1.1
12434  * Copyright(c) 2006-2007, Ext JS, LLC.
12435  *
12436  * Originally Released Under LGPL - original licence link has changed is not relivant.
12437  *
12438  * Fork - LGPL
12439  * <script type="text/javascript">
12440  */
12441 /**
12442  * @class Roo.data.HttpProxy
12443  * @extends Roo.data.DataProxy
12444  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12445  * configured to reference a certain URL.<br><br>
12446  * <p>
12447  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12448  * from which the running page was served.<br><br>
12449  * <p>
12450  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12451  * <p>
12452  * Be aware that to enable the browser to parse an XML document, the server must set
12453  * the Content-Type header in the HTTP response to "text/xml".
12454  * @constructor
12455  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12456  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12457  * will be used to make the request.
12458  */
12459 Roo.data.HttpProxy = function(conn){
12460     Roo.data.HttpProxy.superclass.constructor.call(this);
12461     // is conn a conn config or a real conn?
12462     this.conn = conn;
12463     this.useAjax = !conn || !conn.events;
12464   
12465 };
12466
12467 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12468     // thse are take from connection...
12469     
12470     /**
12471      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12472      */
12473     /**
12474      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12475      * extra parameters to each request made by this object. (defaults to undefined)
12476      */
12477     /**
12478      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12479      *  to each request made by this object. (defaults to undefined)
12480      */
12481     /**
12482      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
12483      */
12484     /**
12485      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12486      */
12487      /**
12488      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12489      * @type Boolean
12490      */
12491   
12492
12493     /**
12494      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12495      * @type Boolean
12496      */
12497     /**
12498      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12499      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12500      * a finer-grained basis than the DataProxy events.
12501      */
12502     getConnection : function(){
12503         return this.useAjax ? Roo.Ajax : this.conn;
12504     },
12505
12506     /**
12507      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12508      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12509      * process that block using the passed callback.
12510      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12511      * for the request to the remote server.
12512      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12513      * object into a block of Roo.data.Records.
12514      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12515      * The function must be passed <ul>
12516      * <li>The Record block object</li>
12517      * <li>The "arg" argument from the load function</li>
12518      * <li>A boolean success indicator</li>
12519      * </ul>
12520      * @param {Object} scope The scope in which to call the callback
12521      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12522      */
12523     load : function(params, reader, callback, scope, arg){
12524         if(this.fireEvent("beforeload", this, params) !== false){
12525             var  o = {
12526                 params : params || {},
12527                 request: {
12528                     callback : callback,
12529                     scope : scope,
12530                     arg : arg
12531                 },
12532                 reader: reader,
12533                 callback : this.loadResponse,
12534                 scope: this
12535             };
12536             if(this.useAjax){
12537                 Roo.applyIf(o, this.conn);
12538                 if(this.activeRequest){
12539                     Roo.Ajax.abort(this.activeRequest);
12540                 }
12541                 this.activeRequest = Roo.Ajax.request(o);
12542             }else{
12543                 this.conn.request(o);
12544             }
12545         }else{
12546             callback.call(scope||this, null, arg, false);
12547         }
12548     },
12549
12550     // private
12551     loadResponse : function(o, success, response){
12552         delete this.activeRequest;
12553         if(!success){
12554             this.fireEvent("loadexception", this, o, response);
12555             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12556             return;
12557         }
12558         var result;
12559         try {
12560             result = o.reader.read(response);
12561         }catch(e){
12562             this.fireEvent("loadexception", this, o, response, e);
12563             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12564             return;
12565         }
12566         
12567         this.fireEvent("load", this, o, o.request.arg);
12568         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12569     },
12570
12571     // private
12572     update : function(dataSet){
12573
12574     },
12575
12576     // private
12577     updateResponse : function(dataSet){
12578
12579     }
12580 });/*
12581  * Based on:
12582  * Ext JS Library 1.1.1
12583  * Copyright(c) 2006-2007, Ext JS, LLC.
12584  *
12585  * Originally Released Under LGPL - original licence link has changed is not relivant.
12586  *
12587  * Fork - LGPL
12588  * <script type="text/javascript">
12589  */
12590
12591 /**
12592  * @class Roo.data.ScriptTagProxy
12593  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12594  * other than the originating domain of the running page.<br><br>
12595  * <p>
12596  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
12597  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12598  * <p>
12599  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12600  * source code that is used as the source inside a &lt;script> tag.<br><br>
12601  * <p>
12602  * In order for the browser to process the returned data, the server must wrap the data object
12603  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12604  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12605  * depending on whether the callback name was passed:
12606  * <p>
12607  * <pre><code>
12608 boolean scriptTag = false;
12609 String cb = request.getParameter("callback");
12610 if (cb != null) {
12611     scriptTag = true;
12612     response.setContentType("text/javascript");
12613 } else {
12614     response.setContentType("application/x-json");
12615 }
12616 Writer out = response.getWriter();
12617 if (scriptTag) {
12618     out.write(cb + "(");
12619 }
12620 out.print(dataBlock.toJsonString());
12621 if (scriptTag) {
12622     out.write(");");
12623 }
12624 </pre></code>
12625  *
12626  * @constructor
12627  * @param {Object} config A configuration object.
12628  */
12629 Roo.data.ScriptTagProxy = function(config){
12630     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12631     Roo.apply(this, config);
12632     this.head = document.getElementsByTagName("head")[0];
12633 };
12634
12635 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12636
12637 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12638     /**
12639      * @cfg {String} url The URL from which to request the data object.
12640      */
12641     /**
12642      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12643      */
12644     timeout : 30000,
12645     /**
12646      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12647      * the server the name of the callback function set up by the load call to process the returned data object.
12648      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12649      * javascript output which calls this named function passing the data object as its only parameter.
12650      */
12651     callbackParam : "callback",
12652     /**
12653      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12654      * name to the request.
12655      */
12656     nocache : true,
12657
12658     /**
12659      * Load data from the configured URL, read the data object into
12660      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12661      * process that block using the passed callback.
12662      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12663      * for the request to the remote server.
12664      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12665      * object into a block of Roo.data.Records.
12666      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12667      * The function must be passed <ul>
12668      * <li>The Record block object</li>
12669      * <li>The "arg" argument from the load function</li>
12670      * <li>A boolean success indicator</li>
12671      * </ul>
12672      * @param {Object} scope The scope in which to call the callback
12673      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12674      */
12675     load : function(params, reader, callback, scope, arg){
12676         if(this.fireEvent("beforeload", this, params) !== false){
12677
12678             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12679
12680             var url = this.url;
12681             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12682             if(this.nocache){
12683                 url += "&_dc=" + (new Date().getTime());
12684             }
12685             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12686             var trans = {
12687                 id : transId,
12688                 cb : "stcCallback"+transId,
12689                 scriptId : "stcScript"+transId,
12690                 params : params,
12691                 arg : arg,
12692                 url : url,
12693                 callback : callback,
12694                 scope : scope,
12695                 reader : reader
12696             };
12697             var conn = this;
12698
12699             window[trans.cb] = function(o){
12700                 conn.handleResponse(o, trans);
12701             };
12702
12703             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12704
12705             if(this.autoAbort !== false){
12706                 this.abort();
12707             }
12708
12709             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12710
12711             var script = document.createElement("script");
12712             script.setAttribute("src", url);
12713             script.setAttribute("type", "text/javascript");
12714             script.setAttribute("id", trans.scriptId);
12715             this.head.appendChild(script);
12716
12717             this.trans = trans;
12718         }else{
12719             callback.call(scope||this, null, arg, false);
12720         }
12721     },
12722
12723     // private
12724     isLoading : function(){
12725         return this.trans ? true : false;
12726     },
12727
12728     /**
12729      * Abort the current server request.
12730      */
12731     abort : function(){
12732         if(this.isLoading()){
12733             this.destroyTrans(this.trans);
12734         }
12735     },
12736
12737     // private
12738     destroyTrans : function(trans, isLoaded){
12739         this.head.removeChild(document.getElementById(trans.scriptId));
12740         clearTimeout(trans.timeoutId);
12741         if(isLoaded){
12742             window[trans.cb] = undefined;
12743             try{
12744                 delete window[trans.cb];
12745             }catch(e){}
12746         }else{
12747             // if hasn't been loaded, wait for load to remove it to prevent script error
12748             window[trans.cb] = function(){
12749                 window[trans.cb] = undefined;
12750                 try{
12751                     delete window[trans.cb];
12752                 }catch(e){}
12753             };
12754         }
12755     },
12756
12757     // private
12758     handleResponse : function(o, trans){
12759         this.trans = false;
12760         this.destroyTrans(trans, true);
12761         var result;
12762         try {
12763             result = trans.reader.readRecords(o);
12764         }catch(e){
12765             this.fireEvent("loadexception", this, o, trans.arg, e);
12766             trans.callback.call(trans.scope||window, null, trans.arg, false);
12767             return;
12768         }
12769         this.fireEvent("load", this, o, trans.arg);
12770         trans.callback.call(trans.scope||window, result, trans.arg, true);
12771     },
12772
12773     // private
12774     handleFailure : function(trans){
12775         this.trans = false;
12776         this.destroyTrans(trans, false);
12777         this.fireEvent("loadexception", this, null, trans.arg);
12778         trans.callback.call(trans.scope||window, null, trans.arg, false);
12779     }
12780 });/*
12781  * Based on:
12782  * Ext JS Library 1.1.1
12783  * Copyright(c) 2006-2007, Ext JS, LLC.
12784  *
12785  * Originally Released Under LGPL - original licence link has changed is not relivant.
12786  *
12787  * Fork - LGPL
12788  * <script type="text/javascript">
12789  */
12790
12791 /**
12792  * @class Roo.data.JsonReader
12793  * @extends Roo.data.DataReader
12794  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12795  * based on mappings in a provided Roo.data.Record constructor.
12796  * 
12797  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12798  * in the reply previously. 
12799  * 
12800  * <p>
12801  * Example code:
12802  * <pre><code>
12803 var RecordDef = Roo.data.Record.create([
12804     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12805     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12806 ]);
12807 var myReader = new Roo.data.JsonReader({
12808     totalProperty: "results",    // The property which contains the total dataset size (optional)
12809     root: "rows",                // The property which contains an Array of row objects
12810     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12811 }, RecordDef);
12812 </code></pre>
12813  * <p>
12814  * This would consume a JSON file like this:
12815  * <pre><code>
12816 { 'results': 2, 'rows': [
12817     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12818     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12819 }
12820 </code></pre>
12821  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12822  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12823  * paged from the remote server.
12824  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12825  * @cfg {String} root name of the property which contains the Array of row objects.
12826  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12827  * @cfg {Array} fields Array of field definition objects
12828  * @constructor
12829  * Create a new JsonReader
12830  * @param {Object} meta Metadata configuration options
12831  * @param {Object} recordType Either an Array of field definition objects,
12832  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12833  */
12834 Roo.data.JsonReader = function(meta, recordType){
12835     
12836     meta = meta || {};
12837     // set some defaults:
12838     Roo.applyIf(meta, {
12839         totalProperty: 'total',
12840         successProperty : 'success',
12841         root : 'data',
12842         id : 'id'
12843     });
12844     
12845     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12846 };
12847 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12848     
12849     /**
12850      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12851      * Used by Store query builder to append _requestMeta to params.
12852      * 
12853      */
12854     metaFromRemote : false,
12855     /**
12856      * This method is only used by a DataProxy which has retrieved data from a remote server.
12857      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12858      * @return {Object} data A data block which is used by an Roo.data.Store object as
12859      * a cache of Roo.data.Records.
12860      */
12861     read : function(response){
12862         var json = response.responseText;
12863        
12864         var o = /* eval:var:o */ eval("("+json+")");
12865         if(!o) {
12866             throw {message: "JsonReader.read: Json object not found"};
12867         }
12868         
12869         if(o.metaData){
12870             
12871             delete this.ef;
12872             this.metaFromRemote = true;
12873             this.meta = o.metaData;
12874             this.recordType = Roo.data.Record.create(o.metaData.fields);
12875             this.onMetaChange(this.meta, this.recordType, o);
12876         }
12877         return this.readRecords(o);
12878     },
12879
12880     // private function a store will implement
12881     onMetaChange : function(meta, recordType, o){
12882
12883     },
12884
12885     /**
12886          * @ignore
12887          */
12888     simpleAccess: function(obj, subsc) {
12889         return obj[subsc];
12890     },
12891
12892         /**
12893          * @ignore
12894          */
12895     getJsonAccessor: function(){
12896         var re = /[\[\.]/;
12897         return function(expr) {
12898             try {
12899                 return(re.test(expr))
12900                     ? new Function("obj", "return obj." + expr)
12901                     : function(obj){
12902                         return obj[expr];
12903                     };
12904             } catch(e){}
12905             return Roo.emptyFn;
12906         };
12907     }(),
12908
12909     /**
12910      * Create a data block containing Roo.data.Records from an XML document.
12911      * @param {Object} o An object which contains an Array of row objects in the property specified
12912      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12913      * which contains the total size of the dataset.
12914      * @return {Object} data A data block which is used by an Roo.data.Store object as
12915      * a cache of Roo.data.Records.
12916      */
12917     readRecords : function(o){
12918         /**
12919          * After any data loads, the raw JSON data is available for further custom processing.
12920          * @type Object
12921          */
12922         this.o = o;
12923         var s = this.meta, Record = this.recordType,
12924             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12925
12926 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12927         if (!this.ef) {
12928             if(s.totalProperty) {
12929                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12930                 }
12931                 if(s.successProperty) {
12932                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12933                 }
12934                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12935                 if (s.id) {
12936                         var g = this.getJsonAccessor(s.id);
12937                         this.getId = function(rec) {
12938                                 var r = g(rec);  
12939                                 return (r === undefined || r === "") ? null : r;
12940                         };
12941                 } else {
12942                         this.getId = function(){return null;};
12943                 }
12944             this.ef = [];
12945             for(var jj = 0; jj < fl; jj++){
12946                 f = fi[jj];
12947                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12948                 this.ef[jj] = this.getJsonAccessor(map);
12949             }
12950         }
12951
12952         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12953         if(s.totalProperty){
12954             var vt = parseInt(this.getTotal(o), 10);
12955             if(!isNaN(vt)){
12956                 totalRecords = vt;
12957             }
12958         }
12959         if(s.successProperty){
12960             var vs = this.getSuccess(o);
12961             if(vs === false || vs === 'false'){
12962                 success = false;
12963             }
12964         }
12965         var records = [];
12966         for(var i = 0; i < c; i++){
12967                 var n = root[i];
12968             var values = {};
12969             var id = this.getId(n);
12970             for(var j = 0; j < fl; j++){
12971                 f = fi[j];
12972             var v = this.ef[j](n);
12973             if (!f.convert) {
12974                 Roo.log('missing convert for ' + f.name);
12975                 Roo.log(f);
12976                 continue;
12977             }
12978             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12979             }
12980             var record = new Record(values, id);
12981             record.json = n;
12982             records[i] = record;
12983         }
12984         return {
12985             raw : o,
12986             success : success,
12987             records : records,
12988             totalRecords : totalRecords
12989         };
12990     }
12991 });/*
12992  * Based on:
12993  * Ext JS Library 1.1.1
12994  * Copyright(c) 2006-2007, Ext JS, LLC.
12995  *
12996  * Originally Released Under LGPL - original licence link has changed is not relivant.
12997  *
12998  * Fork - LGPL
12999  * <script type="text/javascript">
13000  */
13001
13002 /**
13003  * @class Roo.data.ArrayReader
13004  * @extends Roo.data.DataReader
13005  * Data reader class to create an Array of Roo.data.Record objects from an Array.
13006  * Each element of that Array represents a row of data fields. The
13007  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
13008  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
13009  * <p>
13010  * Example code:.
13011  * <pre><code>
13012 var RecordDef = Roo.data.Record.create([
13013     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
13014     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
13015 ]);
13016 var myReader = new Roo.data.ArrayReader({
13017     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
13018 }, RecordDef);
13019 </code></pre>
13020  * <p>
13021  * This would consume an Array like this:
13022  * <pre><code>
13023 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
13024   </code></pre>
13025  
13026  * @constructor
13027  * Create a new JsonReader
13028  * @param {Object} meta Metadata configuration options.
13029  * @param {Object|Array} recordType Either an Array of field definition objects
13030  * 
13031  * @cfg {Array} fields Array of field definition objects
13032  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
13033  * as specified to {@link Roo.data.Record#create},
13034  * or an {@link Roo.data.Record} object
13035  *
13036  * 
13037  * created using {@link Roo.data.Record#create}.
13038  */
13039 Roo.data.ArrayReader = function(meta, recordType){
13040     
13041      
13042     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13043 };
13044
13045 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13046     /**
13047      * Create a data block containing Roo.data.Records from an XML document.
13048      * @param {Object} o An Array of row objects which represents the dataset.
13049      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
13050      * a cache of Roo.data.Records.
13051      */
13052     readRecords : function(o){
13053         var sid = this.meta ? this.meta.id : null;
13054         var recordType = this.recordType, fields = recordType.prototype.fields;
13055         var records = [];
13056         var root = o;
13057             for(var i = 0; i < root.length; i++){
13058                     var n = root[i];
13059                 var values = {};
13060                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13061                 for(var j = 0, jlen = fields.length; j < jlen; j++){
13062                 var f = fields.items[j];
13063                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13064                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13065                 v = f.convert(v);
13066                 values[f.name] = v;
13067             }
13068                 var record = new recordType(values, id);
13069                 record.json = n;
13070                 records[records.length] = record;
13071             }
13072             return {
13073                 records : records,
13074                 totalRecords : records.length
13075             };
13076     }
13077 });/*
13078  * - LGPL
13079  * * 
13080  */
13081
13082 /**
13083  * @class Roo.bootstrap.ComboBox
13084  * @extends Roo.bootstrap.TriggerField
13085  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13086  * @cfg {Boolean} append (true|false) default false
13087  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13088  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13089  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13090  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13091  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13092  * @cfg {Boolean} animate default true
13093  * @cfg {Boolean} emptyResultText only for touch device
13094  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13095  * @cfg {String} emptyTitle default ''
13096  * @constructor
13097  * Create a new ComboBox.
13098  * @param {Object} config Configuration options
13099  */
13100 Roo.bootstrap.ComboBox = function(config){
13101     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13102     this.addEvents({
13103         /**
13104          * @event expand
13105          * Fires when the dropdown list is expanded
13106         * @param {Roo.bootstrap.ComboBox} combo This combo box
13107         */
13108         'expand' : true,
13109         /**
13110          * @event collapse
13111          * Fires when the dropdown list is collapsed
13112         * @param {Roo.bootstrap.ComboBox} combo This combo box
13113         */
13114         'collapse' : true,
13115         /**
13116          * @event beforeselect
13117          * Fires before a list item is selected. Return false to cancel the selection.
13118         * @param {Roo.bootstrap.ComboBox} combo This combo box
13119         * @param {Roo.data.Record} record The data record returned from the underlying store
13120         * @param {Number} index The index of the selected item in the dropdown list
13121         */
13122         'beforeselect' : true,
13123         /**
13124          * @event select
13125          * Fires when a list item is selected
13126         * @param {Roo.bootstrap.ComboBox} combo This combo box
13127         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13128         * @param {Number} index The index of the selected item in the dropdown list
13129         */
13130         'select' : true,
13131         /**
13132          * @event beforequery
13133          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13134          * The event object passed has these properties:
13135         * @param {Roo.bootstrap.ComboBox} combo This combo box
13136         * @param {String} query The query
13137         * @param {Boolean} forceAll true to force "all" query
13138         * @param {Boolean} cancel true to cancel the query
13139         * @param {Object} e The query event object
13140         */
13141         'beforequery': true,
13142          /**
13143          * @event add
13144          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13145         * @param {Roo.bootstrap.ComboBox} combo This combo box
13146         */
13147         'add' : true,
13148         /**
13149          * @event edit
13150          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13151         * @param {Roo.bootstrap.ComboBox} combo This combo box
13152         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13153         */
13154         'edit' : true,
13155         /**
13156          * @event remove
13157          * Fires when the remove value from the combobox array
13158         * @param {Roo.bootstrap.ComboBox} combo This combo box
13159         */
13160         'remove' : true,
13161         /**
13162          * @event afterremove
13163          * Fires when the remove value from the combobox array
13164         * @param {Roo.bootstrap.ComboBox} combo This combo box
13165         */
13166         'afterremove' : true,
13167         /**
13168          * @event specialfilter
13169          * Fires when specialfilter
13170             * @param {Roo.bootstrap.ComboBox} combo This combo box
13171             */
13172         'specialfilter' : true,
13173         /**
13174          * @event tick
13175          * Fires when tick the element
13176             * @param {Roo.bootstrap.ComboBox} combo This combo box
13177             */
13178         'tick' : true,
13179         /**
13180          * @event touchviewdisplay
13181          * Fires when touch view require special display (default is using displayField)
13182             * @param {Roo.bootstrap.ComboBox} combo This combo box
13183             * @param {Object} cfg set html .
13184             */
13185         'touchviewdisplay' : true
13186         
13187     });
13188     
13189     this.item = [];
13190     this.tickItems = [];
13191     
13192     this.selectedIndex = -1;
13193     if(this.mode == 'local'){
13194         if(config.queryDelay === undefined){
13195             this.queryDelay = 10;
13196         }
13197         if(config.minChars === undefined){
13198             this.minChars = 0;
13199         }
13200     }
13201 };
13202
13203 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13204      
13205     /**
13206      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13207      * rendering into an Roo.Editor, defaults to false)
13208      */
13209     /**
13210      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13211      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13212      */
13213     /**
13214      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13215      */
13216     /**
13217      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13218      * the dropdown list (defaults to undefined, with no header element)
13219      */
13220
13221      /**
13222      * @cfg {String/Roo.Template} tpl The template to use to render the output
13223      */
13224      
13225      /**
13226      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13227      */
13228     listWidth: undefined,
13229     /**
13230      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13231      * mode = 'remote' or 'text' if mode = 'local')
13232      */
13233     displayField: undefined,
13234     
13235     /**
13236      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13237      * mode = 'remote' or 'value' if mode = 'local'). 
13238      * Note: use of a valueField requires the user make a selection
13239      * in order for a value to be mapped.
13240      */
13241     valueField: undefined,
13242     /**
13243      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13244      */
13245     modalTitle : '',
13246     
13247     /**
13248      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13249      * field's data value (defaults to the underlying DOM element's name)
13250      */
13251     hiddenName: undefined,
13252     /**
13253      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13254      */
13255     listClass: '',
13256     /**
13257      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13258      */
13259     selectedClass: 'active',
13260     
13261     /**
13262      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13263      */
13264     shadow:'sides',
13265     /**
13266      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13267      * anchor positions (defaults to 'tl-bl')
13268      */
13269     listAlign: 'tl-bl?',
13270     /**
13271      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13272      */
13273     maxHeight: 300,
13274     /**
13275      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13276      * query specified by the allQuery config option (defaults to 'query')
13277      */
13278     triggerAction: 'query',
13279     /**
13280      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13281      * (defaults to 4, does not apply if editable = false)
13282      */
13283     minChars : 4,
13284     /**
13285      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13286      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13287      */
13288     typeAhead: false,
13289     /**
13290      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13291      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13292      */
13293     queryDelay: 500,
13294     /**
13295      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13296      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13297      */
13298     pageSize: 0,
13299     /**
13300      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13301      * when editable = true (defaults to false)
13302      */
13303     selectOnFocus:false,
13304     /**
13305      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13306      */
13307     queryParam: 'query',
13308     /**
13309      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13310      * when mode = 'remote' (defaults to 'Loading...')
13311      */
13312     loadingText: 'Loading...',
13313     /**
13314      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13315      */
13316     resizable: false,
13317     /**
13318      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13319      */
13320     handleHeight : 8,
13321     /**
13322      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13323      * traditional select (defaults to true)
13324      */
13325     editable: true,
13326     /**
13327      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13328      */
13329     allQuery: '',
13330     /**
13331      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13332      */
13333     mode: 'remote',
13334     /**
13335      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13336      * listWidth has a higher value)
13337      */
13338     minListWidth : 70,
13339     /**
13340      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13341      * allow the user to set arbitrary text into the field (defaults to false)
13342      */
13343     forceSelection:false,
13344     /**
13345      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13346      * if typeAhead = true (defaults to 250)
13347      */
13348     typeAheadDelay : 250,
13349     /**
13350      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13351      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13352      */
13353     valueNotFoundText : undefined,
13354     /**
13355      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13356      */
13357     blockFocus : false,
13358     
13359     /**
13360      * @cfg {Boolean} disableClear Disable showing of clear button.
13361      */
13362     disableClear : false,
13363     /**
13364      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13365      */
13366     alwaysQuery : false,
13367     
13368     /**
13369      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13370      */
13371     multiple : false,
13372     
13373     /**
13374      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13375      */
13376     invalidClass : "has-warning",
13377     
13378     /**
13379      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13380      */
13381     validClass : "has-success",
13382     
13383     /**
13384      * @cfg {Boolean} specialFilter (true|false) special filter default false
13385      */
13386     specialFilter : false,
13387     
13388     /**
13389      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13390      */
13391     mobileTouchView : true,
13392     
13393     /**
13394      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13395      */
13396     useNativeIOS : false,
13397     
13398     /**
13399      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13400      */
13401     mobile_restrict_height : false,
13402     
13403     ios_options : false,
13404     
13405     //private
13406     addicon : false,
13407     editicon: false,
13408     
13409     page: 0,
13410     hasQuery: false,
13411     append: false,
13412     loadNext: false,
13413     autoFocus : true,
13414     tickable : false,
13415     btnPosition : 'right',
13416     triggerList : true,
13417     showToggleBtn : true,
13418     animate : true,
13419     emptyResultText: 'Empty',
13420     triggerText : 'Select',
13421     emptyTitle : '',
13422     
13423     // element that contains real text value.. (when hidden is used..)
13424     
13425     getAutoCreate : function()
13426     {   
13427         var cfg = false;
13428         //render
13429         /*
13430          * Render classic select for iso
13431          */
13432         
13433         if(Roo.isIOS && this.useNativeIOS){
13434             cfg = this.getAutoCreateNativeIOS();
13435             return cfg;
13436         }
13437         
13438         /*
13439          * Touch Devices
13440          */
13441         
13442         if(Roo.isTouch && this.mobileTouchView){
13443             cfg = this.getAutoCreateTouchView();
13444             return cfg;;
13445         }
13446         
13447         /*
13448          *  Normal ComboBox
13449          */
13450         if(!this.tickable){
13451             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13452             return cfg;
13453         }
13454         
13455         /*
13456          *  ComboBox with tickable selections
13457          */
13458              
13459         var align = this.labelAlign || this.parentLabelAlign();
13460         
13461         cfg = {
13462             cls : 'form-group roo-combobox-tickable' //input-group
13463         };
13464         
13465         var btn_text_select = '';
13466         var btn_text_done = '';
13467         var btn_text_cancel = '';
13468         
13469         if (this.btn_text_show) {
13470             btn_text_select = 'Select';
13471             btn_text_done = 'Done';
13472             btn_text_cancel = 'Cancel'; 
13473         }
13474         
13475         var buttons = {
13476             tag : 'div',
13477             cls : 'tickable-buttons',
13478             cn : [
13479                 {
13480                     tag : 'button',
13481                     type : 'button',
13482                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13483                     //html : this.triggerText
13484                     html: btn_text_select
13485                 },
13486                 {
13487                     tag : 'button',
13488                     type : 'button',
13489                     name : 'ok',
13490                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13491                     //html : 'Done'
13492                     html: btn_text_done
13493                 },
13494                 {
13495                     tag : 'button',
13496                     type : 'button',
13497                     name : 'cancel',
13498                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13499                     //html : 'Cancel'
13500                     html: btn_text_cancel
13501                 }
13502             ]
13503         };
13504         
13505         if(this.editable){
13506             buttons.cn.unshift({
13507                 tag: 'input',
13508                 cls: 'roo-select2-search-field-input'
13509             });
13510         }
13511         
13512         var _this = this;
13513         
13514         Roo.each(buttons.cn, function(c){
13515             if (_this.size) {
13516                 c.cls += ' btn-' + _this.size;
13517             }
13518
13519             if (_this.disabled) {
13520                 c.disabled = true;
13521             }
13522         });
13523         
13524         var box = {
13525             tag: 'div',
13526             style : 'display: contents',
13527             cn: [
13528                 {
13529                     tag: 'input',
13530                     type : 'hidden',
13531                     cls: 'form-hidden-field'
13532                 },
13533                 {
13534                     tag: 'ul',
13535                     cls: 'roo-select2-choices',
13536                     cn:[
13537                         {
13538                             tag: 'li',
13539                             cls: 'roo-select2-search-field',
13540                             cn: [
13541                                 buttons
13542                             ]
13543                         }
13544                     ]
13545                 }
13546             ]
13547         };
13548         
13549         var combobox = {
13550             cls: 'roo-select2-container input-group roo-select2-container-multi',
13551             cn: [
13552                 
13553                 box
13554 //                {
13555 //                    tag: 'ul',
13556 //                    cls: 'typeahead typeahead-long dropdown-menu',
13557 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13558 //                }
13559             ]
13560         };
13561         
13562         if(this.hasFeedback && !this.allowBlank){
13563             
13564             var feedback = {
13565                 tag: 'span',
13566                 cls: 'glyphicon form-control-feedback'
13567             };
13568
13569             combobox.cn.push(feedback);
13570         }
13571         
13572         var indicator = {
13573             tag : 'i',
13574             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13575             tooltip : 'This field is required'
13576         };
13577         if (Roo.bootstrap.version == 4) {
13578             indicator = {
13579                 tag : 'i',
13580                 style : 'display:none'
13581             };
13582         }
13583         if (align ==='left' && this.fieldLabel.length) {
13584             
13585             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13586             
13587             cfg.cn = [
13588                 indicator,
13589                 {
13590                     tag: 'label',
13591                     'for' :  id,
13592                     cls : 'control-label col-form-label',
13593                     html : this.fieldLabel
13594
13595                 },
13596                 {
13597                     cls : "", 
13598                     cn: [
13599                         combobox
13600                     ]
13601                 }
13602
13603             ];
13604             
13605             var labelCfg = cfg.cn[1];
13606             var contentCfg = cfg.cn[2];
13607             
13608
13609             if(this.indicatorpos == 'right'){
13610                 
13611                 cfg.cn = [
13612                     {
13613                         tag: 'label',
13614                         'for' :  id,
13615                         cls : 'control-label col-form-label',
13616                         cn : [
13617                             {
13618                                 tag : 'span',
13619                                 html : this.fieldLabel
13620                             },
13621                             indicator
13622                         ]
13623                     },
13624                     {
13625                         cls : "",
13626                         cn: [
13627                             combobox
13628                         ]
13629                     }
13630
13631                 ];
13632                 
13633                 
13634                 
13635                 labelCfg = cfg.cn[0];
13636                 contentCfg = cfg.cn[1];
13637             
13638             }
13639             
13640             if(this.labelWidth > 12){
13641                 labelCfg.style = "width: " + this.labelWidth + 'px';
13642             }
13643             
13644             if(this.labelWidth < 13 && this.labelmd == 0){
13645                 this.labelmd = this.labelWidth;
13646             }
13647             
13648             if(this.labellg > 0){
13649                 labelCfg.cls += ' col-lg-' + this.labellg;
13650                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13651             }
13652             
13653             if(this.labelmd > 0){
13654                 labelCfg.cls += ' col-md-' + this.labelmd;
13655                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13656             }
13657             
13658             if(this.labelsm > 0){
13659                 labelCfg.cls += ' col-sm-' + this.labelsm;
13660                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13661             }
13662             
13663             if(this.labelxs > 0){
13664                 labelCfg.cls += ' col-xs-' + this.labelxs;
13665                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13666             }
13667                 
13668                 
13669         } else if ( this.fieldLabel.length) {
13670 //                Roo.log(" label");
13671                  cfg.cn = [
13672                    indicator,
13673                     {
13674                         tag: 'label',
13675                         //cls : 'input-group-addon',
13676                         html : this.fieldLabel
13677                     },
13678                     combobox
13679                 ];
13680                 
13681                 if(this.indicatorpos == 'right'){
13682                     cfg.cn = [
13683                         {
13684                             tag: 'label',
13685                             //cls : 'input-group-addon',
13686                             html : this.fieldLabel
13687                         },
13688                         indicator,
13689                         combobox
13690                     ];
13691                     
13692                 }
13693
13694         } else {
13695             
13696 //                Roo.log(" no label && no align");
13697                 cfg = combobox
13698                      
13699                 
13700         }
13701          
13702         var settings=this;
13703         ['xs','sm','md','lg'].map(function(size){
13704             if (settings[size]) {
13705                 cfg.cls += ' col-' + size + '-' + settings[size];
13706             }
13707         });
13708         
13709         return cfg;
13710         
13711     },
13712     
13713     _initEventsCalled : false,
13714     
13715     // private
13716     initEvents: function()
13717     {   
13718         if (this._initEventsCalled) { // as we call render... prevent looping...
13719             return;
13720         }
13721         this._initEventsCalled = true;
13722         
13723         if (!this.store) {
13724             throw "can not find store for combo";
13725         }
13726         
13727         this.indicator = this.indicatorEl();
13728         
13729         this.store = Roo.factory(this.store, Roo.data);
13730         this.store.parent = this;
13731         
13732         // if we are building from html. then this element is so complex, that we can not really
13733         // use the rendered HTML.
13734         // so we have to trash and replace the previous code.
13735         if (Roo.XComponent.build_from_html) {
13736             // remove this element....
13737             var e = this.el.dom, k=0;
13738             while (e ) { e = e.previousSibling;  ++k;}
13739
13740             this.el.remove();
13741             
13742             this.el=false;
13743             this.rendered = false;
13744             
13745             this.render(this.parent().getChildContainer(true), k);
13746         }
13747         
13748         if(Roo.isIOS && this.useNativeIOS){
13749             this.initIOSView();
13750             return;
13751         }
13752         
13753         /*
13754          * Touch Devices
13755          */
13756         
13757         if(Roo.isTouch && this.mobileTouchView){
13758             this.initTouchView();
13759             return;
13760         }
13761         
13762         if(this.tickable){
13763             this.initTickableEvents();
13764             return;
13765         }
13766         
13767         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13768         
13769         if(this.hiddenName){
13770             
13771             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13772             
13773             this.hiddenField.dom.value =
13774                 this.hiddenValue !== undefined ? this.hiddenValue :
13775                 this.value !== undefined ? this.value : '';
13776
13777             // prevent input submission
13778             this.el.dom.removeAttribute('name');
13779             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13780              
13781              
13782         }
13783         //if(Roo.isGecko){
13784         //    this.el.dom.setAttribute('autocomplete', 'off');
13785         //}
13786         
13787         var cls = 'x-combo-list';
13788         
13789         //this.list = new Roo.Layer({
13790         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13791         //});
13792         
13793         var _this = this;
13794         
13795         (function(){
13796             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13797             _this.list.setWidth(lw);
13798         }).defer(100);
13799         
13800         this.list.on('mouseover', this.onViewOver, this);
13801         this.list.on('mousemove', this.onViewMove, this);
13802         this.list.on('scroll', this.onViewScroll, this);
13803         
13804         /*
13805         this.list.swallowEvent('mousewheel');
13806         this.assetHeight = 0;
13807
13808         if(this.title){
13809             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13810             this.assetHeight += this.header.getHeight();
13811         }
13812
13813         this.innerList = this.list.createChild({cls:cls+'-inner'});
13814         this.innerList.on('mouseover', this.onViewOver, this);
13815         this.innerList.on('mousemove', this.onViewMove, this);
13816         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13817         
13818         if(this.allowBlank && !this.pageSize && !this.disableClear){
13819             this.footer = this.list.createChild({cls:cls+'-ft'});
13820             this.pageTb = new Roo.Toolbar(this.footer);
13821            
13822         }
13823         if(this.pageSize){
13824             this.footer = this.list.createChild({cls:cls+'-ft'});
13825             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13826                     {pageSize: this.pageSize});
13827             
13828         }
13829         
13830         if (this.pageTb && this.allowBlank && !this.disableClear) {
13831             var _this = this;
13832             this.pageTb.add(new Roo.Toolbar.Fill(), {
13833                 cls: 'x-btn-icon x-btn-clear',
13834                 text: '&#160;',
13835                 handler: function()
13836                 {
13837                     _this.collapse();
13838                     _this.clearValue();
13839                     _this.onSelect(false, -1);
13840                 }
13841             });
13842         }
13843         if (this.footer) {
13844             this.assetHeight += this.footer.getHeight();
13845         }
13846         */
13847             
13848         if(!this.tpl){
13849             this.tpl = Roo.bootstrap.version == 4 ?
13850                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13851                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13852         }
13853
13854         this.view = new Roo.View(this.list, this.tpl, {
13855             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13856         });
13857         //this.view.wrapEl.setDisplayed(false);
13858         this.view.on('click', this.onViewClick, this);
13859         
13860         
13861         this.store.on('beforeload', this.onBeforeLoad, this);
13862         this.store.on('load', this.onLoad, this);
13863         this.store.on('loadexception', this.onLoadException, this);
13864         /*
13865         if(this.resizable){
13866             this.resizer = new Roo.Resizable(this.list,  {
13867                pinned:true, handles:'se'
13868             });
13869             this.resizer.on('resize', function(r, w, h){
13870                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13871                 this.listWidth = w;
13872                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13873                 this.restrictHeight();
13874             }, this);
13875             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13876         }
13877         */
13878         if(!this.editable){
13879             this.editable = true;
13880             this.setEditable(false);
13881         }
13882         
13883         /*
13884         
13885         if (typeof(this.events.add.listeners) != 'undefined') {
13886             
13887             this.addicon = this.wrap.createChild(
13888                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13889        
13890             this.addicon.on('click', function(e) {
13891                 this.fireEvent('add', this);
13892             }, this);
13893         }
13894         if (typeof(this.events.edit.listeners) != 'undefined') {
13895             
13896             this.editicon = this.wrap.createChild(
13897                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13898             if (this.addicon) {
13899                 this.editicon.setStyle('margin-left', '40px');
13900             }
13901             this.editicon.on('click', function(e) {
13902                 
13903                 // we fire even  if inothing is selected..
13904                 this.fireEvent('edit', this, this.lastData );
13905                 
13906             }, this);
13907         }
13908         */
13909         
13910         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13911             "up" : function(e){
13912                 this.inKeyMode = true;
13913                 this.selectPrev();
13914             },
13915
13916             "down" : function(e){
13917                 if(!this.isExpanded()){
13918                     this.onTriggerClick();
13919                 }else{
13920                     this.inKeyMode = true;
13921                     this.selectNext();
13922                 }
13923             },
13924
13925             "enter" : function(e){
13926 //                this.onViewClick();
13927                 //return true;
13928                 this.collapse();
13929                 
13930                 if(this.fireEvent("specialkey", this, e)){
13931                     this.onViewClick(false);
13932                 }
13933                 
13934                 return true;
13935             },
13936
13937             "esc" : function(e){
13938                 this.collapse();
13939             },
13940
13941             "tab" : function(e){
13942                 this.collapse();
13943                 
13944                 if(this.fireEvent("specialkey", this, e)){
13945                     this.onViewClick(false);
13946                 }
13947                 
13948                 return true;
13949             },
13950
13951             scope : this,
13952
13953             doRelay : function(foo, bar, hname){
13954                 if(hname == 'down' || this.scope.isExpanded()){
13955                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13956                 }
13957                 return true;
13958             },
13959
13960             forceKeyDown: true
13961         });
13962         
13963         
13964         this.queryDelay = Math.max(this.queryDelay || 10,
13965                 this.mode == 'local' ? 10 : 250);
13966         
13967         
13968         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13969         
13970         if(this.typeAhead){
13971             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13972         }
13973         if(this.editable !== false){
13974             this.inputEl().on("keyup", this.onKeyUp, this);
13975         }
13976         if(this.forceSelection){
13977             this.inputEl().on('blur', this.doForce, this);
13978         }
13979         
13980         if(this.multiple){
13981             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13982             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13983         }
13984     },
13985     
13986     initTickableEvents: function()
13987     {   
13988         this.createList();
13989         
13990         if(this.hiddenName){
13991             
13992             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13993             
13994             this.hiddenField.dom.value =
13995                 this.hiddenValue !== undefined ? this.hiddenValue :
13996                 this.value !== undefined ? this.value : '';
13997
13998             // prevent input submission
13999             this.el.dom.removeAttribute('name');
14000             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14001              
14002              
14003         }
14004         
14005 //        this.list = this.el.select('ul.dropdown-menu',true).first();
14006         
14007         this.choices = this.el.select('ul.roo-select2-choices', true).first();
14008         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14009         if(this.triggerList){
14010             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
14011         }
14012          
14013         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
14014         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
14015         
14016         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
14017         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
14018         
14019         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
14020         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
14021         
14022         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
14023         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
14024         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
14025         
14026         this.okBtn.hide();
14027         this.cancelBtn.hide();
14028         
14029         var _this = this;
14030         
14031         (function(){
14032             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
14033             _this.list.setWidth(lw);
14034         }).defer(100);
14035         
14036         this.list.on('mouseover', this.onViewOver, this);
14037         this.list.on('mousemove', this.onViewMove, this);
14038         
14039         this.list.on('scroll', this.onViewScroll, this);
14040         
14041         if(!this.tpl){
14042             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
14043                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14044         }
14045
14046         this.view = new Roo.View(this.list, this.tpl, {
14047             singleSelect:true,
14048             tickable:true,
14049             parent:this,
14050             store: this.store,
14051             selectedClass: this.selectedClass
14052         });
14053         
14054         //this.view.wrapEl.setDisplayed(false);
14055         this.view.on('click', this.onViewClick, this);
14056         
14057         
14058         
14059         this.store.on('beforeload', this.onBeforeLoad, this);
14060         this.store.on('load', this.onLoad, this);
14061         this.store.on('loadexception', this.onLoadException, this);
14062         
14063         if(this.editable){
14064             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14065                 "up" : function(e){
14066                     this.inKeyMode = true;
14067                     this.selectPrev();
14068                 },
14069
14070                 "down" : function(e){
14071                     this.inKeyMode = true;
14072                     this.selectNext();
14073                 },
14074
14075                 "enter" : function(e){
14076                     if(this.fireEvent("specialkey", this, e)){
14077                         this.onViewClick(false);
14078                     }
14079                     
14080                     return true;
14081                 },
14082
14083                 "esc" : function(e){
14084                     this.onTickableFooterButtonClick(e, false, false);
14085                 },
14086
14087                 "tab" : function(e){
14088                     this.fireEvent("specialkey", this, e);
14089                     
14090                     this.onTickableFooterButtonClick(e, false, false);
14091                     
14092                     return true;
14093                 },
14094
14095                 scope : this,
14096
14097                 doRelay : function(e, fn, key){
14098                     if(this.scope.isExpanded()){
14099                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14100                     }
14101                     return true;
14102                 },
14103
14104                 forceKeyDown: true
14105             });
14106         }
14107         
14108         this.queryDelay = Math.max(this.queryDelay || 10,
14109                 this.mode == 'local' ? 10 : 250);
14110         
14111         
14112         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14113         
14114         if(this.typeAhead){
14115             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14116         }
14117         
14118         if(this.editable !== false){
14119             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14120         }
14121         
14122         this.indicator = this.indicatorEl();
14123         
14124         if(this.indicator){
14125             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14126             this.indicator.hide();
14127         }
14128         
14129     },
14130
14131     onDestroy : function(){
14132         if(this.view){
14133             this.view.setStore(null);
14134             this.view.el.removeAllListeners();
14135             this.view.el.remove();
14136             this.view.purgeListeners();
14137         }
14138         if(this.list){
14139             this.list.dom.innerHTML  = '';
14140         }
14141         
14142         if(this.store){
14143             this.store.un('beforeload', this.onBeforeLoad, this);
14144             this.store.un('load', this.onLoad, this);
14145             this.store.un('loadexception', this.onLoadException, this);
14146         }
14147         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14148     },
14149
14150     // private
14151     fireKey : function(e){
14152         if(e.isNavKeyPress() && !this.list.isVisible()){
14153             this.fireEvent("specialkey", this, e);
14154         }
14155     },
14156
14157     // private
14158     onResize: function(w, h){
14159 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14160 //        
14161 //        if(typeof w != 'number'){
14162 //            // we do not handle it!?!?
14163 //            return;
14164 //        }
14165 //        var tw = this.trigger.getWidth();
14166 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14167 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14168 //        var x = w - tw;
14169 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14170 //            
14171 //        //this.trigger.setStyle('left', x+'px');
14172 //        
14173 //        if(this.list && this.listWidth === undefined){
14174 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14175 //            this.list.setWidth(lw);
14176 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14177 //        }
14178         
14179     
14180         
14181     },
14182
14183     /**
14184      * Allow or prevent the user from directly editing the field text.  If false is passed,
14185      * the user will only be able to select from the items defined in the dropdown list.  This method
14186      * is the runtime equivalent of setting the 'editable' config option at config time.
14187      * @param {Boolean} value True to allow the user to directly edit the field text
14188      */
14189     setEditable : function(value){
14190         if(value == this.editable){
14191             return;
14192         }
14193         this.editable = value;
14194         if(!value){
14195             this.inputEl().dom.setAttribute('readOnly', true);
14196             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14197             this.inputEl().addClass('x-combo-noedit');
14198         }else{
14199             this.inputEl().dom.setAttribute('readOnly', false);
14200             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14201             this.inputEl().removeClass('x-combo-noedit');
14202         }
14203     },
14204
14205     // private
14206     
14207     onBeforeLoad : function(combo,opts){
14208         if(!this.hasFocus){
14209             return;
14210         }
14211          if (!opts.add) {
14212             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14213          }
14214         this.restrictHeight();
14215         this.selectedIndex = -1;
14216     },
14217
14218     // private
14219     onLoad : function(){
14220         
14221         this.hasQuery = false;
14222         
14223         if(!this.hasFocus){
14224             return;
14225         }
14226         
14227         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14228             this.loading.hide();
14229         }
14230         
14231         if(this.store.getCount() > 0){
14232             
14233             this.expand();
14234             this.restrictHeight();
14235             if(this.lastQuery == this.allQuery){
14236                 if(this.editable && !this.tickable){
14237                     this.inputEl().dom.select();
14238                 }
14239                 
14240                 if(
14241                     !this.selectByValue(this.value, true) &&
14242                     this.autoFocus && 
14243                     (
14244                         !this.store.lastOptions ||
14245                         typeof(this.store.lastOptions.add) == 'undefined' || 
14246                         this.store.lastOptions.add != true
14247                     )
14248                 ){
14249                     this.select(0, true);
14250                 }
14251             }else{
14252                 if(this.autoFocus){
14253                     this.selectNext();
14254                 }
14255                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14256                     this.taTask.delay(this.typeAheadDelay);
14257                 }
14258             }
14259         }else{
14260             this.onEmptyResults();
14261         }
14262         
14263         //this.el.focus();
14264     },
14265     // private
14266     onLoadException : function()
14267     {
14268         this.hasQuery = false;
14269         
14270         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14271             this.loading.hide();
14272         }
14273         
14274         if(this.tickable && this.editable){
14275             return;
14276         }
14277         
14278         this.collapse();
14279         // only causes errors at present
14280         //Roo.log(this.store.reader.jsonData);
14281         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14282             // fixme
14283             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14284         //}
14285         
14286         
14287     },
14288     // private
14289     onTypeAhead : function(){
14290         if(this.store.getCount() > 0){
14291             var r = this.store.getAt(0);
14292             var newValue = r.data[this.displayField];
14293             var len = newValue.length;
14294             var selStart = this.getRawValue().length;
14295             
14296             if(selStart != len){
14297                 this.setRawValue(newValue);
14298                 this.selectText(selStart, newValue.length);
14299             }
14300         }
14301     },
14302
14303     // private
14304     onSelect : function(record, index){
14305         
14306         if(this.fireEvent('beforeselect', this, record, index) !== false){
14307         
14308             this.setFromData(index > -1 ? record.data : false);
14309             
14310             this.collapse();
14311             this.fireEvent('select', this, record, index);
14312         }
14313     },
14314
14315     /**
14316      * Returns the currently selected field value or empty string if no value is set.
14317      * @return {String} value The selected value
14318      */
14319     getValue : function()
14320     {
14321         if(Roo.isIOS && this.useNativeIOS){
14322             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14323         }
14324         
14325         if(this.multiple){
14326             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14327         }
14328         
14329         if(this.valueField){
14330             return typeof this.value != 'undefined' ? this.value : '';
14331         }else{
14332             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14333         }
14334     },
14335     
14336     getRawValue : function()
14337     {
14338         if(Roo.isIOS && this.useNativeIOS){
14339             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14340         }
14341         
14342         var v = this.inputEl().getValue();
14343         
14344         return v;
14345     },
14346
14347     /**
14348      * Clears any text/value currently set in the field
14349      */
14350     clearValue : function(){
14351         
14352         if(this.hiddenField){
14353             this.hiddenField.dom.value = '';
14354         }
14355         this.value = '';
14356         this.setRawValue('');
14357         this.lastSelectionText = '';
14358         this.lastData = false;
14359         
14360         var close = this.closeTriggerEl();
14361         
14362         if(close){
14363             close.hide();
14364         }
14365         
14366         this.validate();
14367         
14368     },
14369
14370     /**
14371      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14372      * will be displayed in the field.  If the value does not match the data value of an existing item,
14373      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14374      * Otherwise the field will be blank (although the value will still be set).
14375      * @param {String} value The value to match
14376      */
14377     setValue : function(v)
14378     {
14379         if(Roo.isIOS && this.useNativeIOS){
14380             this.setIOSValue(v);
14381             return;
14382         }
14383         
14384         if(this.multiple){
14385             this.syncValue();
14386             return;
14387         }
14388         
14389         var text = v;
14390         if(this.valueField){
14391             var r = this.findRecord(this.valueField, v);
14392             if(r){
14393                 text = r.data[this.displayField];
14394             }else if(this.valueNotFoundText !== undefined){
14395                 text = this.valueNotFoundText;
14396             }
14397         }
14398         this.lastSelectionText = text;
14399         if(this.hiddenField){
14400             this.hiddenField.dom.value = v;
14401         }
14402         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14403         this.value = v;
14404         
14405         var close = this.closeTriggerEl();
14406         
14407         if(close){
14408             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14409         }
14410         
14411         this.validate();
14412     },
14413     /**
14414      * @property {Object} the last set data for the element
14415      */
14416     
14417     lastData : false,
14418     /**
14419      * Sets the value of the field based on a object which is related to the record format for the store.
14420      * @param {Object} value the value to set as. or false on reset?
14421      */
14422     setFromData : function(o){
14423         
14424         if(this.multiple){
14425             this.addItem(o);
14426             return;
14427         }
14428             
14429         var dv = ''; // display value
14430         var vv = ''; // value value..
14431         this.lastData = o;
14432         if (this.displayField) {
14433             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14434         } else {
14435             // this is an error condition!!!
14436             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14437         }
14438         
14439         if(this.valueField){
14440             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14441         }
14442         
14443         var close = this.closeTriggerEl();
14444         
14445         if(close){
14446             if(dv.length || vv * 1 > 0){
14447                 close.show() ;
14448                 this.blockFocus=true;
14449             } else {
14450                 close.hide();
14451             }             
14452         }
14453         
14454         if(this.hiddenField){
14455             this.hiddenField.dom.value = vv;
14456             
14457             this.lastSelectionText = dv;
14458             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14459             this.value = vv;
14460             return;
14461         }
14462         // no hidden field.. - we store the value in 'value', but still display
14463         // display field!!!!
14464         this.lastSelectionText = dv;
14465         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14466         this.value = vv;
14467         
14468         
14469         
14470     },
14471     // private
14472     reset : function(){
14473         // overridden so that last data is reset..
14474         
14475         if(this.multiple){
14476             this.clearItem();
14477             return;
14478         }
14479         
14480         this.setValue(this.originalValue);
14481         //this.clearInvalid();
14482         this.lastData = false;
14483         if (this.view) {
14484             this.view.clearSelections();
14485         }
14486         
14487         this.validate();
14488     },
14489     // private
14490     findRecord : function(prop, value){
14491         var record;
14492         if(this.store.getCount() > 0){
14493             this.store.each(function(r){
14494                 if(r.data[prop] == value){
14495                     record = r;
14496                     return false;
14497                 }
14498                 return true;
14499             });
14500         }
14501         return record;
14502     },
14503     
14504     getName: function()
14505     {
14506         // returns hidden if it's set..
14507         if (!this.rendered) {return ''};
14508         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14509         
14510     },
14511     // private
14512     onViewMove : function(e, t){
14513         this.inKeyMode = false;
14514     },
14515
14516     // private
14517     onViewOver : function(e, t){
14518         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14519             return;
14520         }
14521         var item = this.view.findItemFromChild(t);
14522         
14523         if(item){
14524             var index = this.view.indexOf(item);
14525             this.select(index, false);
14526         }
14527     },
14528
14529     // private
14530     onViewClick : function(view, doFocus, el, e)
14531     {
14532         var index = this.view.getSelectedIndexes()[0];
14533         
14534         var r = this.store.getAt(index);
14535         
14536         if(this.tickable){
14537             
14538             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14539                 return;
14540             }
14541             
14542             var rm = false;
14543             var _this = this;
14544             
14545             Roo.each(this.tickItems, function(v,k){
14546                 
14547                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14548                     Roo.log(v);
14549                     _this.tickItems.splice(k, 1);
14550                     
14551                     if(typeof(e) == 'undefined' && view == false){
14552                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14553                     }
14554                     
14555                     rm = true;
14556                     return;
14557                 }
14558             });
14559             
14560             if(rm){
14561                 return;
14562             }
14563             
14564             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14565                 this.tickItems.push(r.data);
14566             }
14567             
14568             if(typeof(e) == 'undefined' && view == false){
14569                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14570             }
14571                     
14572             return;
14573         }
14574         
14575         if(r){
14576             this.onSelect(r, index);
14577         }
14578         if(doFocus !== false && !this.blockFocus){
14579             this.inputEl().focus();
14580         }
14581     },
14582
14583     // private
14584     restrictHeight : function(){
14585         //this.innerList.dom.style.height = '';
14586         //var inner = this.innerList.dom;
14587         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14588         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14589         //this.list.beginUpdate();
14590         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14591         this.list.alignTo(this.inputEl(), this.listAlign);
14592         this.list.alignTo(this.inputEl(), this.listAlign);
14593         //this.list.endUpdate();
14594     },
14595
14596     // private
14597     onEmptyResults : function(){
14598         
14599         if(this.tickable && this.editable){
14600             this.hasFocus = false;
14601             this.restrictHeight();
14602             return;
14603         }
14604         
14605         this.collapse();
14606     },
14607
14608     /**
14609      * Returns true if the dropdown list is expanded, else false.
14610      */
14611     isExpanded : function(){
14612         return this.list.isVisible();
14613     },
14614
14615     /**
14616      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14617      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14618      * @param {String} value The data value of the item to select
14619      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14620      * selected item if it is not currently in view (defaults to true)
14621      * @return {Boolean} True if the value matched an item in the list, else false
14622      */
14623     selectByValue : function(v, scrollIntoView){
14624         if(v !== undefined && v !== null){
14625             var r = this.findRecord(this.valueField || this.displayField, v);
14626             if(r){
14627                 this.select(this.store.indexOf(r), scrollIntoView);
14628                 return true;
14629             }
14630         }
14631         return false;
14632     },
14633
14634     /**
14635      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14636      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14637      * @param {Number} index The zero-based index of the list item to select
14638      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14639      * selected item if it is not currently in view (defaults to true)
14640      */
14641     select : function(index, scrollIntoView){
14642         this.selectedIndex = index;
14643         this.view.select(index);
14644         if(scrollIntoView !== false){
14645             var el = this.view.getNode(index);
14646             /*
14647              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14648              */
14649             if(el){
14650                 this.list.scrollChildIntoView(el, false);
14651             }
14652         }
14653     },
14654
14655     // private
14656     selectNext : function(){
14657         var ct = this.store.getCount();
14658         if(ct > 0){
14659             if(this.selectedIndex == -1){
14660                 this.select(0);
14661             }else if(this.selectedIndex < ct-1){
14662                 this.select(this.selectedIndex+1);
14663             }
14664         }
14665     },
14666
14667     // private
14668     selectPrev : function(){
14669         var ct = this.store.getCount();
14670         if(ct > 0){
14671             if(this.selectedIndex == -1){
14672                 this.select(0);
14673             }else if(this.selectedIndex != 0){
14674                 this.select(this.selectedIndex-1);
14675             }
14676         }
14677     },
14678
14679     // private
14680     onKeyUp : function(e){
14681         if(this.editable !== false && !e.isSpecialKey()){
14682             this.lastKey = e.getKey();
14683             this.dqTask.delay(this.queryDelay);
14684         }
14685     },
14686
14687     // private
14688     validateBlur : function(){
14689         return !this.list || !this.list.isVisible();   
14690     },
14691
14692     // private
14693     initQuery : function(){
14694         
14695         var v = this.getRawValue();
14696         
14697         if(this.tickable && this.editable){
14698             v = this.tickableInputEl().getValue();
14699         }
14700         
14701         this.doQuery(v);
14702     },
14703
14704     // private
14705     doForce : function(){
14706         if(this.inputEl().dom.value.length > 0){
14707             this.inputEl().dom.value =
14708                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14709              
14710         }
14711     },
14712
14713     /**
14714      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14715      * query allowing the query action to be canceled if needed.
14716      * @param {String} query The SQL query to execute
14717      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14718      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14719      * saved in the current store (defaults to false)
14720      */
14721     doQuery : function(q, forceAll){
14722         
14723         if(q === undefined || q === null){
14724             q = '';
14725         }
14726         var qe = {
14727             query: q,
14728             forceAll: forceAll,
14729             combo: this,
14730             cancel:false
14731         };
14732         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14733             return false;
14734         }
14735         q = qe.query;
14736         
14737         forceAll = qe.forceAll;
14738         if(forceAll === true || (q.length >= this.minChars)){
14739             
14740             this.hasQuery = true;
14741             
14742             if(this.lastQuery != q || this.alwaysQuery){
14743                 this.lastQuery = q;
14744                 if(this.mode == 'local'){
14745                     this.selectedIndex = -1;
14746                     if(forceAll){
14747                         this.store.clearFilter();
14748                     }else{
14749                         
14750                         if(this.specialFilter){
14751                             this.fireEvent('specialfilter', this);
14752                             this.onLoad();
14753                             return;
14754                         }
14755                         
14756                         this.store.filter(this.displayField, q);
14757                     }
14758                     
14759                     this.store.fireEvent("datachanged", this.store);
14760                     
14761                     this.onLoad();
14762                     
14763                     
14764                 }else{
14765                     
14766                     this.store.baseParams[this.queryParam] = q;
14767                     
14768                     var options = {params : this.getParams(q)};
14769                     
14770                     if(this.loadNext){
14771                         options.add = true;
14772                         options.params.start = this.page * this.pageSize;
14773                     }
14774                     
14775                     this.store.load(options);
14776                     
14777                     /*
14778                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14779                      *  we should expand the list on onLoad
14780                      *  so command out it
14781                      */
14782 //                    this.expand();
14783                 }
14784             }else{
14785                 this.selectedIndex = -1;
14786                 this.onLoad();   
14787             }
14788         }
14789         
14790         this.loadNext = false;
14791     },
14792     
14793     // private
14794     getParams : function(q){
14795         var p = {};
14796         //p[this.queryParam] = q;
14797         
14798         if(this.pageSize){
14799             p.start = 0;
14800             p.limit = this.pageSize;
14801         }
14802         return p;
14803     },
14804
14805     /**
14806      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14807      */
14808     collapse : function(){
14809         if(!this.isExpanded()){
14810             return;
14811         }
14812         
14813         this.list.hide();
14814         
14815         this.hasFocus = false;
14816         
14817         if(this.tickable){
14818             this.okBtn.hide();
14819             this.cancelBtn.hide();
14820             this.trigger.show();
14821             
14822             if(this.editable){
14823                 this.tickableInputEl().dom.value = '';
14824                 this.tickableInputEl().blur();
14825             }
14826             
14827         }
14828         
14829         Roo.get(document).un('mousedown', this.collapseIf, this);
14830         Roo.get(document).un('mousewheel', this.collapseIf, this);
14831         if (!this.editable) {
14832             Roo.get(document).un('keydown', this.listKeyPress, this);
14833         }
14834         this.fireEvent('collapse', this);
14835         
14836         this.validate();
14837     },
14838
14839     // private
14840     collapseIf : function(e){
14841         var in_combo  = e.within(this.el);
14842         var in_list =  e.within(this.list);
14843         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14844         
14845         if (in_combo || in_list || is_list) {
14846             //e.stopPropagation();
14847             return;
14848         }
14849         
14850         if(this.tickable){
14851             this.onTickableFooterButtonClick(e, false, false);
14852         }
14853
14854         this.collapse();
14855         
14856     },
14857
14858     /**
14859      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14860      */
14861     expand : function(){
14862        
14863         if(this.isExpanded() || !this.hasFocus){
14864             return;
14865         }
14866         
14867         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14868         this.list.setWidth(lw);
14869         
14870         Roo.log('expand');
14871         
14872         this.list.show();
14873         
14874         this.restrictHeight();
14875         
14876         if(this.tickable){
14877             
14878             this.tickItems = Roo.apply([], this.item);
14879             
14880             this.okBtn.show();
14881             this.cancelBtn.show();
14882             this.trigger.hide();
14883             
14884             if(this.editable){
14885                 this.tickableInputEl().focus();
14886             }
14887             
14888         }
14889         
14890         Roo.get(document).on('mousedown', this.collapseIf, this);
14891         Roo.get(document).on('mousewheel', this.collapseIf, this);
14892         if (!this.editable) {
14893             Roo.get(document).on('keydown', this.listKeyPress, this);
14894         }
14895         
14896         this.fireEvent('expand', this);
14897     },
14898
14899     // private
14900     // Implements the default empty TriggerField.onTriggerClick function
14901     onTriggerClick : function(e)
14902     {
14903         Roo.log('trigger click');
14904         
14905         if(this.disabled || !this.triggerList){
14906             return;
14907         }
14908         
14909         this.page = 0;
14910         this.loadNext = false;
14911         
14912         if(this.isExpanded()){
14913             this.collapse();
14914             if (!this.blockFocus) {
14915                 this.inputEl().focus();
14916             }
14917             
14918         }else {
14919             this.hasFocus = true;
14920             if(this.triggerAction == 'all') {
14921                 this.doQuery(this.allQuery, true);
14922             } else {
14923                 this.doQuery(this.getRawValue());
14924             }
14925             if (!this.blockFocus) {
14926                 this.inputEl().focus();
14927             }
14928         }
14929     },
14930     
14931     onTickableTriggerClick : function(e)
14932     {
14933         if(this.disabled){
14934             return;
14935         }
14936         
14937         this.page = 0;
14938         this.loadNext = false;
14939         this.hasFocus = true;
14940         
14941         if(this.triggerAction == 'all') {
14942             this.doQuery(this.allQuery, true);
14943         } else {
14944             this.doQuery(this.getRawValue());
14945         }
14946     },
14947     
14948     onSearchFieldClick : function(e)
14949     {
14950         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14951             this.onTickableFooterButtonClick(e, false, false);
14952             return;
14953         }
14954         
14955         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14956             return;
14957         }
14958         
14959         this.page = 0;
14960         this.loadNext = false;
14961         this.hasFocus = true;
14962         
14963         if(this.triggerAction == 'all') {
14964             this.doQuery(this.allQuery, true);
14965         } else {
14966             this.doQuery(this.getRawValue());
14967         }
14968     },
14969     
14970     listKeyPress : function(e)
14971     {
14972         //Roo.log('listkeypress');
14973         // scroll to first matching element based on key pres..
14974         if (e.isSpecialKey()) {
14975             return false;
14976         }
14977         var k = String.fromCharCode(e.getKey()).toUpperCase();
14978         //Roo.log(k);
14979         var match  = false;
14980         var csel = this.view.getSelectedNodes();
14981         var cselitem = false;
14982         if (csel.length) {
14983             var ix = this.view.indexOf(csel[0]);
14984             cselitem  = this.store.getAt(ix);
14985             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14986                 cselitem = false;
14987             }
14988             
14989         }
14990         
14991         this.store.each(function(v) { 
14992             if (cselitem) {
14993                 // start at existing selection.
14994                 if (cselitem.id == v.id) {
14995                     cselitem = false;
14996                 }
14997                 return true;
14998             }
14999                 
15000             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
15001                 match = this.store.indexOf(v);
15002                 return false;
15003             }
15004             return true;
15005         }, this);
15006         
15007         if (match === false) {
15008             return true; // no more action?
15009         }
15010         // scroll to?
15011         this.view.select(match);
15012         var sn = Roo.get(this.view.getSelectedNodes()[0]);
15013         sn.scrollIntoView(sn.dom.parentNode, false);
15014     },
15015     
15016     onViewScroll : function(e, t){
15017         
15018         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
15019             return;
15020         }
15021         
15022         this.hasQuery = true;
15023         
15024         this.loading = this.list.select('.loading', true).first();
15025         
15026         if(this.loading === null){
15027             this.list.createChild({
15028                 tag: 'div',
15029                 cls: 'loading roo-select2-more-results roo-select2-active',
15030                 html: 'Loading more results...'
15031             });
15032             
15033             this.loading = this.list.select('.loading', true).first();
15034             
15035             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
15036             
15037             this.loading.hide();
15038         }
15039         
15040         this.loading.show();
15041         
15042         var _combo = this;
15043         
15044         this.page++;
15045         this.loadNext = true;
15046         
15047         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15048         
15049         return;
15050     },
15051     
15052     addItem : function(o)
15053     {   
15054         var dv = ''; // display value
15055         
15056         if (this.displayField) {
15057             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15058         } else {
15059             // this is an error condition!!!
15060             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15061         }
15062         
15063         if(!dv.length){
15064             return;
15065         }
15066         
15067         var choice = this.choices.createChild({
15068             tag: 'li',
15069             cls: 'roo-select2-search-choice',
15070             cn: [
15071                 {
15072                     tag: 'div',
15073                     html: dv
15074                 },
15075                 {
15076                     tag: 'a',
15077                     href: '#',
15078                     cls: 'roo-select2-search-choice-close fa fa-times',
15079                     tabindex: '-1'
15080                 }
15081             ]
15082             
15083         }, this.searchField);
15084         
15085         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15086         
15087         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15088         
15089         this.item.push(o);
15090         
15091         this.lastData = o;
15092         
15093         this.syncValue();
15094         
15095         this.inputEl().dom.value = '';
15096         
15097         this.validate();
15098     },
15099     
15100     onRemoveItem : function(e, _self, o)
15101     {
15102         e.preventDefault();
15103         
15104         this.lastItem = Roo.apply([], this.item);
15105         
15106         var index = this.item.indexOf(o.data) * 1;
15107         
15108         if( index < 0){
15109             Roo.log('not this item?!');
15110             return;
15111         }
15112         
15113         this.item.splice(index, 1);
15114         o.item.remove();
15115         
15116         this.syncValue();
15117         
15118         this.fireEvent('remove', this, e);
15119         
15120         this.validate();
15121         
15122     },
15123     
15124     syncValue : function()
15125     {
15126         if(!this.item.length){
15127             this.clearValue();
15128             return;
15129         }
15130             
15131         var value = [];
15132         var _this = this;
15133         Roo.each(this.item, function(i){
15134             if(_this.valueField){
15135                 value.push(i[_this.valueField]);
15136                 return;
15137             }
15138
15139             value.push(i);
15140         });
15141
15142         this.value = value.join(',');
15143
15144         if(this.hiddenField){
15145             this.hiddenField.dom.value = this.value;
15146         }
15147         
15148         this.store.fireEvent("datachanged", this.store);
15149         
15150         this.validate();
15151     },
15152     
15153     clearItem : function()
15154     {
15155         if(!this.multiple){
15156             return;
15157         }
15158         
15159         this.item = [];
15160         
15161         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15162            c.remove();
15163         });
15164         
15165         this.syncValue();
15166         
15167         this.validate();
15168         
15169         if(this.tickable && !Roo.isTouch){
15170             this.view.refresh();
15171         }
15172     },
15173     
15174     inputEl: function ()
15175     {
15176         if(Roo.isIOS && this.useNativeIOS){
15177             return this.el.select('select.roo-ios-select', true).first();
15178         }
15179         
15180         if(Roo.isTouch && this.mobileTouchView){
15181             return this.el.select('input.form-control',true).first();
15182         }
15183         
15184         if(this.tickable){
15185             return this.searchField;
15186         }
15187         
15188         return this.el.select('input.form-control',true).first();
15189     },
15190     
15191     onTickableFooterButtonClick : function(e, btn, el)
15192     {
15193         e.preventDefault();
15194         
15195         this.lastItem = Roo.apply([], this.item);
15196         
15197         if(btn && btn.name == 'cancel'){
15198             this.tickItems = Roo.apply([], this.item);
15199             this.collapse();
15200             return;
15201         }
15202         
15203         this.clearItem();
15204         
15205         var _this = this;
15206         
15207         Roo.each(this.tickItems, function(o){
15208             _this.addItem(o);
15209         });
15210         
15211         this.collapse();
15212         
15213     },
15214     
15215     validate : function()
15216     {
15217         if(this.getVisibilityEl().hasClass('hidden')){
15218             return true;
15219         }
15220         
15221         var v = this.getRawValue();
15222         
15223         if(this.multiple){
15224             v = this.getValue();
15225         }
15226         
15227         if(this.disabled || this.allowBlank || v.length){
15228             this.markValid();
15229             return true;
15230         }
15231         
15232         this.markInvalid();
15233         return false;
15234     },
15235     
15236     tickableInputEl : function()
15237     {
15238         if(!this.tickable || !this.editable){
15239             return this.inputEl();
15240         }
15241         
15242         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15243     },
15244     
15245     
15246     getAutoCreateTouchView : function()
15247     {
15248         var id = Roo.id();
15249         
15250         var cfg = {
15251             cls: 'form-group' //input-group
15252         };
15253         
15254         var input =  {
15255             tag: 'input',
15256             id : id,
15257             type : this.inputType,
15258             cls : 'form-control x-combo-noedit',
15259             autocomplete: 'new-password',
15260             placeholder : this.placeholder || '',
15261             readonly : true
15262         };
15263         
15264         if (this.name) {
15265             input.name = this.name;
15266         }
15267         
15268         if (this.size) {
15269             input.cls += ' input-' + this.size;
15270         }
15271         
15272         if (this.disabled) {
15273             input.disabled = true;
15274         }
15275         
15276         var inputblock = {
15277             cls : '',
15278             cn : [
15279                 input
15280             ]
15281         };
15282         
15283         if(this.before){
15284             inputblock.cls += ' input-group';
15285             
15286             inputblock.cn.unshift({
15287                 tag :'span',
15288                 cls : 'input-group-addon input-group-prepend input-group-text',
15289                 html : this.before
15290             });
15291         }
15292         
15293         if(this.removable && !this.multiple){
15294             inputblock.cls += ' roo-removable';
15295             
15296             inputblock.cn.push({
15297                 tag: 'button',
15298                 html : 'x',
15299                 cls : 'roo-combo-removable-btn close'
15300             });
15301         }
15302
15303         if(this.hasFeedback && !this.allowBlank){
15304             
15305             inputblock.cls += ' has-feedback';
15306             
15307             inputblock.cn.push({
15308                 tag: 'span',
15309                 cls: 'glyphicon form-control-feedback'
15310             });
15311             
15312         }
15313         
15314         if (this.after) {
15315             
15316             inputblock.cls += (this.before) ? '' : ' input-group';
15317             
15318             inputblock.cn.push({
15319                 tag :'span',
15320                 cls : 'input-group-addon input-group-append input-group-text',
15321                 html : this.after
15322             });
15323         }
15324
15325         
15326         var ibwrap = inputblock;
15327         
15328         if(this.multiple){
15329             ibwrap = {
15330                 tag: 'ul',
15331                 cls: 'roo-select2-choices',
15332                 cn:[
15333                     {
15334                         tag: 'li',
15335                         cls: 'roo-select2-search-field',
15336                         cn: [
15337
15338                             inputblock
15339                         ]
15340                     }
15341                 ]
15342             };
15343         
15344             
15345         }
15346         
15347         var combobox = {
15348             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15349             cn: [
15350                 {
15351                     tag: 'input',
15352                     type : 'hidden',
15353                     cls: 'form-hidden-field'
15354                 },
15355                 ibwrap
15356             ]
15357         };
15358         
15359         if(!this.multiple && this.showToggleBtn){
15360             
15361             var caret = {
15362                         tag: 'span',
15363                         cls: 'caret'
15364             };
15365             
15366             if (this.caret != false) {
15367                 caret = {
15368                      tag: 'i',
15369                      cls: 'fa fa-' + this.caret
15370                 };
15371                 
15372             }
15373             
15374             combobox.cn.push({
15375                 tag :'span',
15376                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15377                 cn : [
15378                     caret,
15379                     {
15380                         tag: 'span',
15381                         cls: 'combobox-clear',
15382                         cn  : [
15383                             {
15384                                 tag : 'i',
15385                                 cls: 'icon-remove'
15386                             }
15387                         ]
15388                     }
15389                 ]
15390
15391             })
15392         }
15393         
15394         if(this.multiple){
15395             combobox.cls += ' roo-select2-container-multi';
15396         }
15397         
15398         var align = this.labelAlign || this.parentLabelAlign();
15399         
15400         if (align ==='left' && this.fieldLabel.length) {
15401
15402             cfg.cn = [
15403                 {
15404                    tag : 'i',
15405                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15406                    tooltip : 'This field is required'
15407                 },
15408                 {
15409                     tag: 'label',
15410                     cls : 'control-label col-form-label',
15411                     html : this.fieldLabel
15412
15413                 },
15414                 {
15415                     cls : '', 
15416                     cn: [
15417                         combobox
15418                     ]
15419                 }
15420             ];
15421             
15422             var labelCfg = cfg.cn[1];
15423             var contentCfg = cfg.cn[2];
15424             
15425
15426             if(this.indicatorpos == 'right'){
15427                 cfg.cn = [
15428                     {
15429                         tag: 'label',
15430                         'for' :  id,
15431                         cls : 'control-label col-form-label',
15432                         cn : [
15433                             {
15434                                 tag : 'span',
15435                                 html : this.fieldLabel
15436                             },
15437                             {
15438                                 tag : 'i',
15439                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15440                                 tooltip : 'This field is required'
15441                             }
15442                         ]
15443                     },
15444                     {
15445                         cls : "",
15446                         cn: [
15447                             combobox
15448                         ]
15449                     }
15450
15451                 ];
15452                 
15453                 labelCfg = cfg.cn[0];
15454                 contentCfg = cfg.cn[1];
15455             }
15456             
15457            
15458             
15459             if(this.labelWidth > 12){
15460                 labelCfg.style = "width: " + this.labelWidth + 'px';
15461             }
15462             
15463             if(this.labelWidth < 13 && this.labelmd == 0){
15464                 this.labelmd = this.labelWidth;
15465             }
15466             
15467             if(this.labellg > 0){
15468                 labelCfg.cls += ' col-lg-' + this.labellg;
15469                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15470             }
15471             
15472             if(this.labelmd > 0){
15473                 labelCfg.cls += ' col-md-' + this.labelmd;
15474                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15475             }
15476             
15477             if(this.labelsm > 0){
15478                 labelCfg.cls += ' col-sm-' + this.labelsm;
15479                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15480             }
15481             
15482             if(this.labelxs > 0){
15483                 labelCfg.cls += ' col-xs-' + this.labelxs;
15484                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15485             }
15486                 
15487                 
15488         } else if ( this.fieldLabel.length) {
15489             cfg.cn = [
15490                 {
15491                    tag : 'i',
15492                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15493                    tooltip : 'This field is required'
15494                 },
15495                 {
15496                     tag: 'label',
15497                     cls : 'control-label',
15498                     html : this.fieldLabel
15499
15500                 },
15501                 {
15502                     cls : '', 
15503                     cn: [
15504                         combobox
15505                     ]
15506                 }
15507             ];
15508             
15509             if(this.indicatorpos == 'right'){
15510                 cfg.cn = [
15511                     {
15512                         tag: 'label',
15513                         cls : 'control-label',
15514                         html : this.fieldLabel,
15515                         cn : [
15516                             {
15517                                tag : 'i',
15518                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15519                                tooltip : 'This field is required'
15520                             }
15521                         ]
15522                     },
15523                     {
15524                         cls : '', 
15525                         cn: [
15526                             combobox
15527                         ]
15528                     }
15529                 ];
15530             }
15531         } else {
15532             cfg.cn = combobox;    
15533         }
15534         
15535         
15536         var settings = this;
15537         
15538         ['xs','sm','md','lg'].map(function(size){
15539             if (settings[size]) {
15540                 cfg.cls += ' col-' + size + '-' + settings[size];
15541             }
15542         });
15543         
15544         return cfg;
15545     },
15546     
15547     initTouchView : function()
15548     {
15549         this.renderTouchView();
15550         
15551         this.touchViewEl.on('scroll', function(){
15552             this.el.dom.scrollTop = 0;
15553         }, this);
15554         
15555         this.originalValue = this.getValue();
15556         
15557         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15558         
15559         this.inputEl().on("click", this.showTouchView, this);
15560         if (this.triggerEl) {
15561             this.triggerEl.on("click", this.showTouchView, this);
15562         }
15563         
15564         
15565         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15566         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15567         
15568         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15569         
15570         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15571         this.store.on('load', this.onTouchViewLoad, this);
15572         this.store.on('loadexception', this.onTouchViewLoadException, this);
15573         
15574         if(this.hiddenName){
15575             
15576             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15577             
15578             this.hiddenField.dom.value =
15579                 this.hiddenValue !== undefined ? this.hiddenValue :
15580                 this.value !== undefined ? this.value : '';
15581         
15582             this.el.dom.removeAttribute('name');
15583             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15584         }
15585         
15586         if(this.multiple){
15587             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15588             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15589         }
15590         
15591         if(this.removable && !this.multiple){
15592             var close = this.closeTriggerEl();
15593             if(close){
15594                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15595                 close.on('click', this.removeBtnClick, this, close);
15596             }
15597         }
15598         /*
15599          * fix the bug in Safari iOS8
15600          */
15601         this.inputEl().on("focus", function(e){
15602             document.activeElement.blur();
15603         }, this);
15604         
15605         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15606         
15607         return;
15608         
15609         
15610     },
15611     
15612     renderTouchView : function()
15613     {
15614         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15615         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15616         
15617         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15618         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15619         
15620         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15621         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15622         this.touchViewBodyEl.setStyle('overflow', 'auto');
15623         
15624         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15625         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15626         
15627         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15628         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15629         
15630     },
15631     
15632     showTouchView : function()
15633     {
15634         if(this.disabled){
15635             return;
15636         }
15637         
15638         this.touchViewHeaderEl.hide();
15639
15640         if(this.modalTitle.length){
15641             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15642             this.touchViewHeaderEl.show();
15643         }
15644
15645         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15646         this.touchViewEl.show();
15647
15648         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15649         
15650         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15651         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15652
15653         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15654
15655         if(this.modalTitle.length){
15656             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15657         }
15658         
15659         this.touchViewBodyEl.setHeight(bodyHeight);
15660
15661         if(this.animate){
15662             var _this = this;
15663             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15664         }else{
15665             this.touchViewEl.addClass('in');
15666         }
15667         
15668         if(this._touchViewMask){
15669             Roo.get(document.body).addClass("x-body-masked");
15670             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15671             this._touchViewMask.setStyle('z-index', 10000);
15672             this._touchViewMask.addClass('show');
15673         }
15674         
15675         this.doTouchViewQuery();
15676         
15677     },
15678     
15679     hideTouchView : function()
15680     {
15681         this.touchViewEl.removeClass('in');
15682
15683         if(this.animate){
15684             var _this = this;
15685             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15686         }else{
15687             this.touchViewEl.setStyle('display', 'none');
15688         }
15689         
15690         if(this._touchViewMask){
15691             this._touchViewMask.removeClass('show');
15692             Roo.get(document.body).removeClass("x-body-masked");
15693         }
15694     },
15695     
15696     setTouchViewValue : function()
15697     {
15698         if(this.multiple){
15699             this.clearItem();
15700         
15701             var _this = this;
15702
15703             Roo.each(this.tickItems, function(o){
15704                 this.addItem(o);
15705             }, this);
15706         }
15707         
15708         this.hideTouchView();
15709     },
15710     
15711     doTouchViewQuery : function()
15712     {
15713         var qe = {
15714             query: '',
15715             forceAll: true,
15716             combo: this,
15717             cancel:false
15718         };
15719         
15720         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15721             return false;
15722         }
15723         
15724         if(!this.alwaysQuery || this.mode == 'local'){
15725             this.onTouchViewLoad();
15726             return;
15727         }
15728         
15729         this.store.load();
15730     },
15731     
15732     onTouchViewBeforeLoad : function(combo,opts)
15733     {
15734         return;
15735     },
15736
15737     // private
15738     onTouchViewLoad : function()
15739     {
15740         if(this.store.getCount() < 1){
15741             this.onTouchViewEmptyResults();
15742             return;
15743         }
15744         
15745         this.clearTouchView();
15746         
15747         var rawValue = this.getRawValue();
15748         
15749         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15750         
15751         this.tickItems = [];
15752         
15753         this.store.data.each(function(d, rowIndex){
15754             var row = this.touchViewListGroup.createChild(template);
15755             
15756             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15757                 row.addClass(d.data.cls);
15758             }
15759             
15760             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15761                 var cfg = {
15762                     data : d.data,
15763                     html : d.data[this.displayField]
15764                 };
15765                 
15766                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15767                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15768                 }
15769             }
15770             row.removeClass('selected');
15771             if(!this.multiple && this.valueField &&
15772                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15773             {
15774                 // radio buttons..
15775                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15776                 row.addClass('selected');
15777             }
15778             
15779             if(this.multiple && this.valueField &&
15780                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15781             {
15782                 
15783                 // checkboxes...
15784                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15785                 this.tickItems.push(d.data);
15786             }
15787             
15788             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15789             
15790         }, this);
15791         
15792         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15793         
15794         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15795
15796         if(this.modalTitle.length){
15797             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15798         }
15799
15800         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15801         
15802         if(this.mobile_restrict_height && listHeight < bodyHeight){
15803             this.touchViewBodyEl.setHeight(listHeight);
15804         }
15805         
15806         var _this = this;
15807         
15808         if(firstChecked && listHeight > bodyHeight){
15809             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15810         }
15811         
15812     },
15813     
15814     onTouchViewLoadException : function()
15815     {
15816         this.hideTouchView();
15817     },
15818     
15819     onTouchViewEmptyResults : function()
15820     {
15821         this.clearTouchView();
15822         
15823         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15824         
15825         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15826         
15827     },
15828     
15829     clearTouchView : function()
15830     {
15831         this.touchViewListGroup.dom.innerHTML = '';
15832     },
15833     
15834     onTouchViewClick : function(e, el, o)
15835     {
15836         e.preventDefault();
15837         
15838         var row = o.row;
15839         var rowIndex = o.rowIndex;
15840         
15841         var r = this.store.getAt(rowIndex);
15842         
15843         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15844             
15845             if(!this.multiple){
15846                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15847                     c.dom.removeAttribute('checked');
15848                 }, this);
15849
15850                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15851
15852                 this.setFromData(r.data);
15853
15854                 var close = this.closeTriggerEl();
15855
15856                 if(close){
15857                     close.show();
15858                 }
15859
15860                 this.hideTouchView();
15861
15862                 this.fireEvent('select', this, r, rowIndex);
15863
15864                 return;
15865             }
15866
15867             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15868                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15869                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15870                 return;
15871             }
15872
15873             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15874             this.addItem(r.data);
15875             this.tickItems.push(r.data);
15876         }
15877     },
15878     
15879     getAutoCreateNativeIOS : function()
15880     {
15881         var cfg = {
15882             cls: 'form-group' //input-group,
15883         };
15884         
15885         var combobox =  {
15886             tag: 'select',
15887             cls : 'roo-ios-select'
15888         };
15889         
15890         if (this.name) {
15891             combobox.name = this.name;
15892         }
15893         
15894         if (this.disabled) {
15895             combobox.disabled = true;
15896         }
15897         
15898         var settings = this;
15899         
15900         ['xs','sm','md','lg'].map(function(size){
15901             if (settings[size]) {
15902                 cfg.cls += ' col-' + size + '-' + settings[size];
15903             }
15904         });
15905         
15906         cfg.cn = combobox;
15907         
15908         return cfg;
15909         
15910     },
15911     
15912     initIOSView : function()
15913     {
15914         this.store.on('load', this.onIOSViewLoad, this);
15915         
15916         return;
15917     },
15918     
15919     onIOSViewLoad : function()
15920     {
15921         if(this.store.getCount() < 1){
15922             return;
15923         }
15924         
15925         this.clearIOSView();
15926         
15927         if(this.allowBlank) {
15928             
15929             var default_text = '-- SELECT --';
15930             
15931             if(this.placeholder.length){
15932                 default_text = this.placeholder;
15933             }
15934             
15935             if(this.emptyTitle.length){
15936                 default_text += ' - ' + this.emptyTitle + ' -';
15937             }
15938             
15939             var opt = this.inputEl().createChild({
15940                 tag: 'option',
15941                 value : 0,
15942                 html : default_text
15943             });
15944             
15945             var o = {};
15946             o[this.valueField] = 0;
15947             o[this.displayField] = default_text;
15948             
15949             this.ios_options.push({
15950                 data : o,
15951                 el : opt
15952             });
15953             
15954         }
15955         
15956         this.store.data.each(function(d, rowIndex){
15957             
15958             var html = '';
15959             
15960             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15961                 html = d.data[this.displayField];
15962             }
15963             
15964             var value = '';
15965             
15966             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15967                 value = d.data[this.valueField];
15968             }
15969             
15970             var option = {
15971                 tag: 'option',
15972                 value : value,
15973                 html : html
15974             };
15975             
15976             if(this.value == d.data[this.valueField]){
15977                 option['selected'] = true;
15978             }
15979             
15980             var opt = this.inputEl().createChild(option);
15981             
15982             this.ios_options.push({
15983                 data : d.data,
15984                 el : opt
15985             });
15986             
15987         }, this);
15988         
15989         this.inputEl().on('change', function(){
15990            this.fireEvent('select', this);
15991         }, this);
15992         
15993     },
15994     
15995     clearIOSView: function()
15996     {
15997         this.inputEl().dom.innerHTML = '';
15998         
15999         this.ios_options = [];
16000     },
16001     
16002     setIOSValue: function(v)
16003     {
16004         this.value = v;
16005         
16006         if(!this.ios_options){
16007             return;
16008         }
16009         
16010         Roo.each(this.ios_options, function(opts){
16011            
16012            opts.el.dom.removeAttribute('selected');
16013            
16014            if(opts.data[this.valueField] != v){
16015                return;
16016            }
16017            
16018            opts.el.dom.setAttribute('selected', true);
16019            
16020         }, this);
16021     }
16022
16023     /** 
16024     * @cfg {Boolean} grow 
16025     * @hide 
16026     */
16027     /** 
16028     * @cfg {Number} growMin 
16029     * @hide 
16030     */
16031     /** 
16032     * @cfg {Number} growMax 
16033     * @hide 
16034     */
16035     /**
16036      * @hide
16037      * @method autoSize
16038      */
16039 });
16040
16041 Roo.apply(Roo.bootstrap.ComboBox,  {
16042     
16043     header : {
16044         tag: 'div',
16045         cls: 'modal-header',
16046         cn: [
16047             {
16048                 tag: 'h4',
16049                 cls: 'modal-title'
16050             }
16051         ]
16052     },
16053     
16054     body : {
16055         tag: 'div',
16056         cls: 'modal-body',
16057         cn: [
16058             {
16059                 tag: 'ul',
16060                 cls: 'list-group'
16061             }
16062         ]
16063     },
16064     
16065     listItemRadio : {
16066         tag: 'li',
16067         cls: 'list-group-item',
16068         cn: [
16069             {
16070                 tag: 'span',
16071                 cls: 'roo-combobox-list-group-item-value'
16072             },
16073             {
16074                 tag: 'div',
16075                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16076                 cn: [
16077                     {
16078                         tag: 'input',
16079                         type: 'radio'
16080                     },
16081                     {
16082                         tag: 'label'
16083                     }
16084                 ]
16085             }
16086         ]
16087     },
16088     
16089     listItemCheckbox : {
16090         tag: 'li',
16091         cls: 'list-group-item',
16092         cn: [
16093             {
16094                 tag: 'span',
16095                 cls: 'roo-combobox-list-group-item-value'
16096             },
16097             {
16098                 tag: 'div',
16099                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16100                 cn: [
16101                     {
16102                         tag: 'input',
16103                         type: 'checkbox'
16104                     },
16105                     {
16106                         tag: 'label'
16107                     }
16108                 ]
16109             }
16110         ]
16111     },
16112     
16113     emptyResult : {
16114         tag: 'div',
16115         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16116     },
16117     
16118     footer : {
16119         tag: 'div',
16120         cls: 'modal-footer',
16121         cn: [
16122             {
16123                 tag: 'div',
16124                 cls: 'row',
16125                 cn: [
16126                     {
16127                         tag: 'div',
16128                         cls: 'col-xs-6 text-left',
16129                         cn: {
16130                             tag: 'button',
16131                             cls: 'btn btn-danger roo-touch-view-cancel',
16132                             html: 'Cancel'
16133                         }
16134                     },
16135                     {
16136                         tag: 'div',
16137                         cls: 'col-xs-6 text-right',
16138                         cn: {
16139                             tag: 'button',
16140                             cls: 'btn btn-success roo-touch-view-ok',
16141                             html: 'OK'
16142                         }
16143                     }
16144                 ]
16145             }
16146         ]
16147         
16148     }
16149 });
16150
16151 Roo.apply(Roo.bootstrap.ComboBox,  {
16152     
16153     touchViewTemplate : {
16154         tag: 'div',
16155         cls: 'modal fade roo-combobox-touch-view',
16156         cn: [
16157             {
16158                 tag: 'div',
16159                 cls: 'modal-dialog',
16160                 style : 'position:fixed', // we have to fix position....
16161                 cn: [
16162                     {
16163                         tag: 'div',
16164                         cls: 'modal-content',
16165                         cn: [
16166                             Roo.bootstrap.ComboBox.header,
16167                             Roo.bootstrap.ComboBox.body,
16168                             Roo.bootstrap.ComboBox.footer
16169                         ]
16170                     }
16171                 ]
16172             }
16173         ]
16174     }
16175 });/*
16176  * Based on:
16177  * Ext JS Library 1.1.1
16178  * Copyright(c) 2006-2007, Ext JS, LLC.
16179  *
16180  * Originally Released Under LGPL - original licence link has changed is not relivant.
16181  *
16182  * Fork - LGPL
16183  * <script type="text/javascript">
16184  */
16185
16186 /**
16187  * @class Roo.View
16188  * @extends Roo.util.Observable
16189  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16190  * This class also supports single and multi selection modes. <br>
16191  * Create a data model bound view:
16192  <pre><code>
16193  var store = new Roo.data.Store(...);
16194
16195  var view = new Roo.View({
16196     el : "my-element",
16197     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16198  
16199     singleSelect: true,
16200     selectedClass: "ydataview-selected",
16201     store: store
16202  });
16203
16204  // listen for node click?
16205  view.on("click", function(vw, index, node, e){
16206  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16207  });
16208
16209  // load XML data
16210  dataModel.load("foobar.xml");
16211  </code></pre>
16212  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16213  * <br><br>
16214  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16215  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16216  * 
16217  * Note: old style constructor is still suported (container, template, config)
16218  * 
16219  * @constructor
16220  * Create a new View
16221  * @param {Object} config The config object
16222  * 
16223  */
16224 Roo.View = function(config, depreciated_tpl, depreciated_config){
16225     
16226     this.parent = false;
16227     
16228     if (typeof(depreciated_tpl) == 'undefined') {
16229         // new way.. - universal constructor.
16230         Roo.apply(this, config);
16231         this.el  = Roo.get(this.el);
16232     } else {
16233         // old format..
16234         this.el  = Roo.get(config);
16235         this.tpl = depreciated_tpl;
16236         Roo.apply(this, depreciated_config);
16237     }
16238     this.wrapEl  = this.el.wrap().wrap();
16239     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16240     
16241     
16242     if(typeof(this.tpl) == "string"){
16243         this.tpl = new Roo.Template(this.tpl);
16244     } else {
16245         // support xtype ctors..
16246         this.tpl = new Roo.factory(this.tpl, Roo);
16247     }
16248     
16249     
16250     this.tpl.compile();
16251     
16252     /** @private */
16253     this.addEvents({
16254         /**
16255          * @event beforeclick
16256          * Fires before a click is processed. Returns false to cancel the default action.
16257          * @param {Roo.View} this
16258          * @param {Number} index The index of the target node
16259          * @param {HTMLElement} node The target node
16260          * @param {Roo.EventObject} e The raw event object
16261          */
16262             "beforeclick" : true,
16263         /**
16264          * @event click
16265          * Fires when a template node is clicked.
16266          * @param {Roo.View} this
16267          * @param {Number} index The index of the target node
16268          * @param {HTMLElement} node The target node
16269          * @param {Roo.EventObject} e The raw event object
16270          */
16271             "click" : true,
16272         /**
16273          * @event dblclick
16274          * Fires when a template node is double clicked.
16275          * @param {Roo.View} this
16276          * @param {Number} index The index of the target node
16277          * @param {HTMLElement} node The target node
16278          * @param {Roo.EventObject} e The raw event object
16279          */
16280             "dblclick" : true,
16281         /**
16282          * @event contextmenu
16283          * Fires when a template node is right clicked.
16284          * @param {Roo.View} this
16285          * @param {Number} index The index of the target node
16286          * @param {HTMLElement} node The target node
16287          * @param {Roo.EventObject} e The raw event object
16288          */
16289             "contextmenu" : true,
16290         /**
16291          * @event selectionchange
16292          * Fires when the selected nodes change.
16293          * @param {Roo.View} this
16294          * @param {Array} selections Array of the selected nodes
16295          */
16296             "selectionchange" : true,
16297     
16298         /**
16299          * @event beforeselect
16300          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16301          * @param {Roo.View} this
16302          * @param {HTMLElement} node The node to be selected
16303          * @param {Array} selections Array of currently selected nodes
16304          */
16305             "beforeselect" : true,
16306         /**
16307          * @event preparedata
16308          * Fires on every row to render, to allow you to change the data.
16309          * @param {Roo.View} this
16310          * @param {Object} data to be rendered (change this)
16311          */
16312           "preparedata" : true
16313           
16314           
16315         });
16316
16317
16318
16319     this.el.on({
16320         "click": this.onClick,
16321         "dblclick": this.onDblClick,
16322         "contextmenu": this.onContextMenu,
16323         scope:this
16324     });
16325
16326     this.selections = [];
16327     this.nodes = [];
16328     this.cmp = new Roo.CompositeElementLite([]);
16329     if(this.store){
16330         this.store = Roo.factory(this.store, Roo.data);
16331         this.setStore(this.store, true);
16332     }
16333     
16334     if ( this.footer && this.footer.xtype) {
16335            
16336          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16337         
16338         this.footer.dataSource = this.store;
16339         this.footer.container = fctr;
16340         this.footer = Roo.factory(this.footer, Roo);
16341         fctr.insertFirst(this.el);
16342         
16343         // this is a bit insane - as the paging toolbar seems to detach the el..
16344 //        dom.parentNode.parentNode.parentNode
16345          // they get detached?
16346     }
16347     
16348     
16349     Roo.View.superclass.constructor.call(this);
16350     
16351     
16352 };
16353
16354 Roo.extend(Roo.View, Roo.util.Observable, {
16355     
16356      /**
16357      * @cfg {Roo.data.Store} store Data store to load data from.
16358      */
16359     store : false,
16360     
16361     /**
16362      * @cfg {String|Roo.Element} el The container element.
16363      */
16364     el : '',
16365     
16366     /**
16367      * @cfg {String|Roo.Template} tpl The template used by this View 
16368      */
16369     tpl : false,
16370     /**
16371      * @cfg {String} dataName the named area of the template to use as the data area
16372      *                          Works with domtemplates roo-name="name"
16373      */
16374     dataName: false,
16375     /**
16376      * @cfg {String} selectedClass The css class to add to selected nodes
16377      */
16378     selectedClass : "x-view-selected",
16379      /**
16380      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16381      */
16382     emptyText : "",
16383     
16384     /**
16385      * @cfg {String} text to display on mask (default Loading)
16386      */
16387     mask : false,
16388     /**
16389      * @cfg {Boolean} multiSelect Allow multiple selection
16390      */
16391     multiSelect : false,
16392     /**
16393      * @cfg {Boolean} singleSelect Allow single selection
16394      */
16395     singleSelect:  false,
16396     
16397     /**
16398      * @cfg {Boolean} toggleSelect - selecting 
16399      */
16400     toggleSelect : false,
16401     
16402     /**
16403      * @cfg {Boolean} tickable - selecting 
16404      */
16405     tickable : false,
16406     
16407     /**
16408      * Returns the element this view is bound to.
16409      * @return {Roo.Element}
16410      */
16411     getEl : function(){
16412         return this.wrapEl;
16413     },
16414     
16415     
16416
16417     /**
16418      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16419      */
16420     refresh : function(){
16421         //Roo.log('refresh');
16422         var t = this.tpl;
16423         
16424         // if we are using something like 'domtemplate', then
16425         // the what gets used is:
16426         // t.applySubtemplate(NAME, data, wrapping data..)
16427         // the outer template then get' applied with
16428         //     the store 'extra data'
16429         // and the body get's added to the
16430         //      roo-name="data" node?
16431         //      <span class='roo-tpl-{name}'></span> ?????
16432         
16433         
16434         
16435         this.clearSelections();
16436         this.el.update("");
16437         var html = [];
16438         var records = this.store.getRange();
16439         if(records.length < 1) {
16440             
16441             // is this valid??  = should it render a template??
16442             
16443             this.el.update(this.emptyText);
16444             return;
16445         }
16446         var el = this.el;
16447         if (this.dataName) {
16448             this.el.update(t.apply(this.store.meta)); //????
16449             el = this.el.child('.roo-tpl-' + this.dataName);
16450         }
16451         
16452         for(var i = 0, len = records.length; i < len; i++){
16453             var data = this.prepareData(records[i].data, i, records[i]);
16454             this.fireEvent("preparedata", this, data, i, records[i]);
16455             
16456             var d = Roo.apply({}, data);
16457             
16458             if(this.tickable){
16459                 Roo.apply(d, {'roo-id' : Roo.id()});
16460                 
16461                 var _this = this;
16462             
16463                 Roo.each(this.parent.item, function(item){
16464                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16465                         return;
16466                     }
16467                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16468                 });
16469             }
16470             
16471             html[html.length] = Roo.util.Format.trim(
16472                 this.dataName ?
16473                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16474                     t.apply(d)
16475             );
16476         }
16477         
16478         
16479         
16480         el.update(html.join(""));
16481         this.nodes = el.dom.childNodes;
16482         this.updateIndexes(0);
16483     },
16484     
16485
16486     /**
16487      * Function to override to reformat the data that is sent to
16488      * the template for each node.
16489      * DEPRICATED - use the preparedata event handler.
16490      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16491      * a JSON object for an UpdateManager bound view).
16492      */
16493     prepareData : function(data, index, record)
16494     {
16495         this.fireEvent("preparedata", this, data, index, record);
16496         return data;
16497     },
16498
16499     onUpdate : function(ds, record){
16500         // Roo.log('on update');   
16501         this.clearSelections();
16502         var index = this.store.indexOf(record);
16503         var n = this.nodes[index];
16504         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16505         n.parentNode.removeChild(n);
16506         this.updateIndexes(index, index);
16507     },
16508
16509     
16510     
16511 // --------- FIXME     
16512     onAdd : function(ds, records, index)
16513     {
16514         //Roo.log(['on Add', ds, records, index] );        
16515         this.clearSelections();
16516         if(this.nodes.length == 0){
16517             this.refresh();
16518             return;
16519         }
16520         var n = this.nodes[index];
16521         for(var i = 0, len = records.length; i < len; i++){
16522             var d = this.prepareData(records[i].data, i, records[i]);
16523             if(n){
16524                 this.tpl.insertBefore(n, d);
16525             }else{
16526                 
16527                 this.tpl.append(this.el, d);
16528             }
16529         }
16530         this.updateIndexes(index);
16531     },
16532
16533     onRemove : function(ds, record, index){
16534        // Roo.log('onRemove');
16535         this.clearSelections();
16536         var el = this.dataName  ?
16537             this.el.child('.roo-tpl-' + this.dataName) :
16538             this.el; 
16539         
16540         el.dom.removeChild(this.nodes[index]);
16541         this.updateIndexes(index);
16542     },
16543
16544     /**
16545      * Refresh an individual node.
16546      * @param {Number} index
16547      */
16548     refreshNode : function(index){
16549         this.onUpdate(this.store, this.store.getAt(index));
16550     },
16551
16552     updateIndexes : function(startIndex, endIndex){
16553         var ns = this.nodes;
16554         startIndex = startIndex || 0;
16555         endIndex = endIndex || ns.length - 1;
16556         for(var i = startIndex; i <= endIndex; i++){
16557             ns[i].nodeIndex = i;
16558         }
16559     },
16560
16561     /**
16562      * Changes the data store this view uses and refresh the view.
16563      * @param {Store} store
16564      */
16565     setStore : function(store, initial){
16566         if(!initial && this.store){
16567             this.store.un("datachanged", this.refresh);
16568             this.store.un("add", this.onAdd);
16569             this.store.un("remove", this.onRemove);
16570             this.store.un("update", this.onUpdate);
16571             this.store.un("clear", this.refresh);
16572             this.store.un("beforeload", this.onBeforeLoad);
16573             this.store.un("load", this.onLoad);
16574             this.store.un("loadexception", this.onLoad);
16575         }
16576         if(store){
16577           
16578             store.on("datachanged", this.refresh, this);
16579             store.on("add", this.onAdd, this);
16580             store.on("remove", this.onRemove, this);
16581             store.on("update", this.onUpdate, this);
16582             store.on("clear", this.refresh, this);
16583             store.on("beforeload", this.onBeforeLoad, this);
16584             store.on("load", this.onLoad, this);
16585             store.on("loadexception", this.onLoad, this);
16586         }
16587         
16588         if(store){
16589             this.refresh();
16590         }
16591     },
16592     /**
16593      * onbeforeLoad - masks the loading area.
16594      *
16595      */
16596     onBeforeLoad : function(store,opts)
16597     {
16598          //Roo.log('onBeforeLoad');   
16599         if (!opts.add) {
16600             this.el.update("");
16601         }
16602         this.el.mask(this.mask ? this.mask : "Loading" ); 
16603     },
16604     onLoad : function ()
16605     {
16606         this.el.unmask();
16607     },
16608     
16609
16610     /**
16611      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16612      * @param {HTMLElement} node
16613      * @return {HTMLElement} The template node
16614      */
16615     findItemFromChild : function(node){
16616         var el = this.dataName  ?
16617             this.el.child('.roo-tpl-' + this.dataName,true) :
16618             this.el.dom; 
16619         
16620         if(!node || node.parentNode == el){
16621                     return node;
16622             }
16623             var p = node.parentNode;
16624             while(p && p != el){
16625             if(p.parentNode == el){
16626                 return p;
16627             }
16628             p = p.parentNode;
16629         }
16630             return null;
16631     },
16632
16633     /** @ignore */
16634     onClick : function(e){
16635         var item = this.findItemFromChild(e.getTarget());
16636         if(item){
16637             var index = this.indexOf(item);
16638             if(this.onItemClick(item, index, e) !== false){
16639                 this.fireEvent("click", this, index, item, e);
16640             }
16641         }else{
16642             this.clearSelections();
16643         }
16644     },
16645
16646     /** @ignore */
16647     onContextMenu : function(e){
16648         var item = this.findItemFromChild(e.getTarget());
16649         if(item){
16650             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16651         }
16652     },
16653
16654     /** @ignore */
16655     onDblClick : function(e){
16656         var item = this.findItemFromChild(e.getTarget());
16657         if(item){
16658             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16659         }
16660     },
16661
16662     onItemClick : function(item, index, e)
16663     {
16664         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16665             return false;
16666         }
16667         if (this.toggleSelect) {
16668             var m = this.isSelected(item) ? 'unselect' : 'select';
16669             //Roo.log(m);
16670             var _t = this;
16671             _t[m](item, true, false);
16672             return true;
16673         }
16674         if(this.multiSelect || this.singleSelect){
16675             if(this.multiSelect && e.shiftKey && this.lastSelection){
16676                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16677             }else{
16678                 this.select(item, this.multiSelect && e.ctrlKey);
16679                 this.lastSelection = item;
16680             }
16681             
16682             if(!this.tickable){
16683                 e.preventDefault();
16684             }
16685             
16686         }
16687         return true;
16688     },
16689
16690     /**
16691      * Get the number of selected nodes.
16692      * @return {Number}
16693      */
16694     getSelectionCount : function(){
16695         return this.selections.length;
16696     },
16697
16698     /**
16699      * Get the currently selected nodes.
16700      * @return {Array} An array of HTMLElements
16701      */
16702     getSelectedNodes : function(){
16703         return this.selections;
16704     },
16705
16706     /**
16707      * Get the indexes of the selected nodes.
16708      * @return {Array}
16709      */
16710     getSelectedIndexes : function(){
16711         var indexes = [], s = this.selections;
16712         for(var i = 0, len = s.length; i < len; i++){
16713             indexes.push(s[i].nodeIndex);
16714         }
16715         return indexes;
16716     },
16717
16718     /**
16719      * Clear all selections
16720      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16721      */
16722     clearSelections : function(suppressEvent){
16723         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16724             this.cmp.elements = this.selections;
16725             this.cmp.removeClass(this.selectedClass);
16726             this.selections = [];
16727             if(!suppressEvent){
16728                 this.fireEvent("selectionchange", this, this.selections);
16729             }
16730         }
16731     },
16732
16733     /**
16734      * Returns true if the passed node is selected
16735      * @param {HTMLElement/Number} node The node or node index
16736      * @return {Boolean}
16737      */
16738     isSelected : function(node){
16739         var s = this.selections;
16740         if(s.length < 1){
16741             return false;
16742         }
16743         node = this.getNode(node);
16744         return s.indexOf(node) !== -1;
16745     },
16746
16747     /**
16748      * Selects nodes.
16749      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
16750      * @param {Boolean} keepExisting (optional) true to keep existing selections
16751      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16752      */
16753     select : function(nodeInfo, keepExisting, suppressEvent){
16754         if(nodeInfo instanceof Array){
16755             if(!keepExisting){
16756                 this.clearSelections(true);
16757             }
16758             for(var i = 0, len = nodeInfo.length; i < len; i++){
16759                 this.select(nodeInfo[i], true, true);
16760             }
16761             return;
16762         } 
16763         var node = this.getNode(nodeInfo);
16764         if(!node || this.isSelected(node)){
16765             return; // already selected.
16766         }
16767         if(!keepExisting){
16768             this.clearSelections(true);
16769         }
16770         
16771         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16772             Roo.fly(node).addClass(this.selectedClass);
16773             this.selections.push(node);
16774             if(!suppressEvent){
16775                 this.fireEvent("selectionchange", this, this.selections);
16776             }
16777         }
16778         
16779         
16780     },
16781       /**
16782      * Unselects nodes.
16783      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
16784      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16785      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16786      */
16787     unselect : function(nodeInfo, keepExisting, suppressEvent)
16788     {
16789         if(nodeInfo instanceof Array){
16790             Roo.each(this.selections, function(s) {
16791                 this.unselect(s, nodeInfo);
16792             }, this);
16793             return;
16794         }
16795         var node = this.getNode(nodeInfo);
16796         if(!node || !this.isSelected(node)){
16797             //Roo.log("not selected");
16798             return; // not selected.
16799         }
16800         // fireevent???
16801         var ns = [];
16802         Roo.each(this.selections, function(s) {
16803             if (s == node ) {
16804                 Roo.fly(node).removeClass(this.selectedClass);
16805
16806                 return;
16807             }
16808             ns.push(s);
16809         },this);
16810         
16811         this.selections= ns;
16812         this.fireEvent("selectionchange", this, this.selections);
16813     },
16814
16815     /**
16816      * Gets a template node.
16817      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16818      * @return {HTMLElement} The node or null if it wasn't found
16819      */
16820     getNode : function(nodeInfo){
16821         if(typeof nodeInfo == "string"){
16822             return document.getElementById(nodeInfo);
16823         }else if(typeof nodeInfo == "number"){
16824             return this.nodes[nodeInfo];
16825         }
16826         return nodeInfo;
16827     },
16828
16829     /**
16830      * Gets a range template nodes.
16831      * @param {Number} startIndex
16832      * @param {Number} endIndex
16833      * @return {Array} An array of nodes
16834      */
16835     getNodes : function(start, end){
16836         var ns = this.nodes;
16837         start = start || 0;
16838         end = typeof end == "undefined" ? ns.length - 1 : end;
16839         var nodes = [];
16840         if(start <= end){
16841             for(var i = start; i <= end; i++){
16842                 nodes.push(ns[i]);
16843             }
16844         } else{
16845             for(var i = start; i >= end; i--){
16846                 nodes.push(ns[i]);
16847             }
16848         }
16849         return nodes;
16850     },
16851
16852     /**
16853      * Finds the index of the passed node
16854      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16855      * @return {Number} The index of the node or -1
16856      */
16857     indexOf : function(node){
16858         node = this.getNode(node);
16859         if(typeof node.nodeIndex == "number"){
16860             return node.nodeIndex;
16861         }
16862         var ns = this.nodes;
16863         for(var i = 0, len = ns.length; i < len; i++){
16864             if(ns[i] == node){
16865                 return i;
16866             }
16867         }
16868         return -1;
16869     }
16870 });
16871 /*
16872  * - LGPL
16873  *
16874  * based on jquery fullcalendar
16875  * 
16876  */
16877
16878 Roo.bootstrap = Roo.bootstrap || {};
16879 /**
16880  * @class Roo.bootstrap.Calendar
16881  * @extends Roo.bootstrap.Component
16882  * Bootstrap Calendar class
16883  * @cfg {Boolean} loadMask (true|false) default false
16884  * @cfg {Object} header generate the user specific header of the calendar, default false
16885
16886  * @constructor
16887  * Create a new Container
16888  * @param {Object} config The config object
16889  */
16890
16891
16892
16893 Roo.bootstrap.Calendar = function(config){
16894     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16895      this.addEvents({
16896         /**
16897              * @event select
16898              * Fires when a date is selected
16899              * @param {DatePicker} this
16900              * @param {Date} date The selected date
16901              */
16902         'select': true,
16903         /**
16904              * @event monthchange
16905              * Fires when the displayed month changes 
16906              * @param {DatePicker} this
16907              * @param {Date} date The selected month
16908              */
16909         'monthchange': true,
16910         /**
16911              * @event evententer
16912              * Fires when mouse over an event
16913              * @param {Calendar} this
16914              * @param {event} Event
16915              */
16916         'evententer': true,
16917         /**
16918              * @event eventleave
16919              * Fires when the mouse leaves an
16920              * @param {Calendar} this
16921              * @param {event}
16922              */
16923         'eventleave': true,
16924         /**
16925              * @event eventclick
16926              * Fires when the mouse click an
16927              * @param {Calendar} this
16928              * @param {event}
16929              */
16930         'eventclick': true
16931         
16932     });
16933
16934 };
16935
16936 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16937     
16938      /**
16939      * @cfg {Number} startDay
16940      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16941      */
16942     startDay : 0,
16943     
16944     loadMask : false,
16945     
16946     header : false,
16947       
16948     getAutoCreate : function(){
16949         
16950         
16951         var fc_button = function(name, corner, style, content ) {
16952             return Roo.apply({},{
16953                 tag : 'span',
16954                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16955                          (corner.length ?
16956                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16957                             ''
16958                         ),
16959                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16960                 unselectable: 'on'
16961             });
16962         };
16963         
16964         var header = {};
16965         
16966         if(!this.header){
16967             header = {
16968                 tag : 'table',
16969                 cls : 'fc-header',
16970                 style : 'width:100%',
16971                 cn : [
16972                     {
16973                         tag: 'tr',
16974                         cn : [
16975                             {
16976                                 tag : 'td',
16977                                 cls : 'fc-header-left',
16978                                 cn : [
16979                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16980                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16981                                     { tag: 'span', cls: 'fc-header-space' },
16982                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16983
16984
16985                                 ]
16986                             },
16987
16988                             {
16989                                 tag : 'td',
16990                                 cls : 'fc-header-center',
16991                                 cn : [
16992                                     {
16993                                         tag: 'span',
16994                                         cls: 'fc-header-title',
16995                                         cn : {
16996                                             tag: 'H2',
16997                                             html : 'month / year'
16998                                         }
16999                                     }
17000
17001                                 ]
17002                             },
17003                             {
17004                                 tag : 'td',
17005                                 cls : 'fc-header-right',
17006                                 cn : [
17007                               /*      fc_button('month', 'left', '', 'month' ),
17008                                     fc_button('week', '', '', 'week' ),
17009                                     fc_button('day', 'right', '', 'day' )
17010                                 */    
17011
17012                                 ]
17013                             }
17014
17015                         ]
17016                     }
17017                 ]
17018             };
17019         }
17020         
17021         header = this.header;
17022         
17023        
17024         var cal_heads = function() {
17025             var ret = [];
17026             // fixme - handle this.
17027             
17028             for (var i =0; i < Date.dayNames.length; i++) {
17029                 var d = Date.dayNames[i];
17030                 ret.push({
17031                     tag: 'th',
17032                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
17033                     html : d.substring(0,3)
17034                 });
17035                 
17036             }
17037             ret[0].cls += ' fc-first';
17038             ret[6].cls += ' fc-last';
17039             return ret;
17040         };
17041         var cal_cell = function(n) {
17042             return  {
17043                 tag: 'td',
17044                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17045                 cn : [
17046                     {
17047                         cn : [
17048                             {
17049                                 cls: 'fc-day-number',
17050                                 html: 'D'
17051                             },
17052                             {
17053                                 cls: 'fc-day-content',
17054                              
17055                                 cn : [
17056                                      {
17057                                         style: 'position: relative;' // height: 17px;
17058                                     }
17059                                 ]
17060                             }
17061                             
17062                             
17063                         ]
17064                     }
17065                 ]
17066                 
17067             }
17068         };
17069         var cal_rows = function() {
17070             
17071             var ret = [];
17072             for (var r = 0; r < 6; r++) {
17073                 var row= {
17074                     tag : 'tr',
17075                     cls : 'fc-week',
17076                     cn : []
17077                 };
17078                 
17079                 for (var i =0; i < Date.dayNames.length; i++) {
17080                     var d = Date.dayNames[i];
17081                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17082
17083                 }
17084                 row.cn[0].cls+=' fc-first';
17085                 row.cn[0].cn[0].style = 'min-height:90px';
17086                 row.cn[6].cls+=' fc-last';
17087                 ret.push(row);
17088                 
17089             }
17090             ret[0].cls += ' fc-first';
17091             ret[4].cls += ' fc-prev-last';
17092             ret[5].cls += ' fc-last';
17093             return ret;
17094             
17095         };
17096         
17097         var cal_table = {
17098             tag: 'table',
17099             cls: 'fc-border-separate',
17100             style : 'width:100%',
17101             cellspacing  : 0,
17102             cn : [
17103                 { 
17104                     tag: 'thead',
17105                     cn : [
17106                         { 
17107                             tag: 'tr',
17108                             cls : 'fc-first fc-last',
17109                             cn : cal_heads()
17110                         }
17111                     ]
17112                 },
17113                 { 
17114                     tag: 'tbody',
17115                     cn : cal_rows()
17116                 }
17117                   
17118             ]
17119         };
17120          
17121          var cfg = {
17122             cls : 'fc fc-ltr',
17123             cn : [
17124                 header,
17125                 {
17126                     cls : 'fc-content',
17127                     style : "position: relative;",
17128                     cn : [
17129                         {
17130                             cls : 'fc-view fc-view-month fc-grid',
17131                             style : 'position: relative',
17132                             unselectable : 'on',
17133                             cn : [
17134                                 {
17135                                     cls : 'fc-event-container',
17136                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17137                                 },
17138                                 cal_table
17139                             ]
17140                         }
17141                     ]
17142     
17143                 }
17144            ] 
17145             
17146         };
17147         
17148          
17149         
17150         return cfg;
17151     },
17152     
17153     
17154     initEvents : function()
17155     {
17156         if(!this.store){
17157             throw "can not find store for calendar";
17158         }
17159         
17160         var mark = {
17161             tag: "div",
17162             cls:"x-dlg-mask",
17163             style: "text-align:center",
17164             cn: [
17165                 {
17166                     tag: "div",
17167                     style: "background-color:white;width:50%;margin:250 auto",
17168                     cn: [
17169                         {
17170                             tag: "img",
17171                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17172                         },
17173                         {
17174                             tag: "span",
17175                             html: "Loading"
17176                         }
17177                         
17178                     ]
17179                 }
17180             ]
17181         };
17182         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17183         
17184         var size = this.el.select('.fc-content', true).first().getSize();
17185         this.maskEl.setSize(size.width, size.height);
17186         this.maskEl.enableDisplayMode("block");
17187         if(!this.loadMask){
17188             this.maskEl.hide();
17189         }
17190         
17191         this.store = Roo.factory(this.store, Roo.data);
17192         this.store.on('load', this.onLoad, this);
17193         this.store.on('beforeload', this.onBeforeLoad, this);
17194         
17195         this.resize();
17196         
17197         this.cells = this.el.select('.fc-day',true);
17198         //Roo.log(this.cells);
17199         this.textNodes = this.el.query('.fc-day-number');
17200         this.cells.addClassOnOver('fc-state-hover');
17201         
17202         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17203         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17204         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17205         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17206         
17207         this.on('monthchange', this.onMonthChange, this);
17208         
17209         this.update(new Date().clearTime());
17210     },
17211     
17212     resize : function() {
17213         var sz  = this.el.getSize();
17214         
17215         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17216         this.el.select('.fc-day-content div',true).setHeight(34);
17217     },
17218     
17219     
17220     // private
17221     showPrevMonth : function(e){
17222         this.update(this.activeDate.add("mo", -1));
17223     },
17224     showToday : function(e){
17225         this.update(new Date().clearTime());
17226     },
17227     // private
17228     showNextMonth : function(e){
17229         this.update(this.activeDate.add("mo", 1));
17230     },
17231
17232     // private
17233     showPrevYear : function(){
17234         this.update(this.activeDate.add("y", -1));
17235     },
17236
17237     // private
17238     showNextYear : function(){
17239         this.update(this.activeDate.add("y", 1));
17240     },
17241
17242     
17243    // private
17244     update : function(date)
17245     {
17246         var vd = this.activeDate;
17247         this.activeDate = date;
17248 //        if(vd && this.el){
17249 //            var t = date.getTime();
17250 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17251 //                Roo.log('using add remove');
17252 //                
17253 //                this.fireEvent('monthchange', this, date);
17254 //                
17255 //                this.cells.removeClass("fc-state-highlight");
17256 //                this.cells.each(function(c){
17257 //                   if(c.dateValue == t){
17258 //                       c.addClass("fc-state-highlight");
17259 //                       setTimeout(function(){
17260 //                            try{c.dom.firstChild.focus();}catch(e){}
17261 //                       }, 50);
17262 //                       return false;
17263 //                   }
17264 //                   return true;
17265 //                });
17266 //                return;
17267 //            }
17268 //        }
17269         
17270         var days = date.getDaysInMonth();
17271         
17272         var firstOfMonth = date.getFirstDateOfMonth();
17273         var startingPos = firstOfMonth.getDay()-this.startDay;
17274         
17275         if(startingPos < this.startDay){
17276             startingPos += 7;
17277         }
17278         
17279         var pm = date.add(Date.MONTH, -1);
17280         var prevStart = pm.getDaysInMonth()-startingPos;
17281 //        
17282         this.cells = this.el.select('.fc-day',true);
17283         this.textNodes = this.el.query('.fc-day-number');
17284         this.cells.addClassOnOver('fc-state-hover');
17285         
17286         var cells = this.cells.elements;
17287         var textEls = this.textNodes;
17288         
17289         Roo.each(cells, function(cell){
17290             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17291         });
17292         
17293         days += startingPos;
17294
17295         // convert everything to numbers so it's fast
17296         var day = 86400000;
17297         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17298         //Roo.log(d);
17299         //Roo.log(pm);
17300         //Roo.log(prevStart);
17301         
17302         var today = new Date().clearTime().getTime();
17303         var sel = date.clearTime().getTime();
17304         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17305         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17306         var ddMatch = this.disabledDatesRE;
17307         var ddText = this.disabledDatesText;
17308         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17309         var ddaysText = this.disabledDaysText;
17310         var format = this.format;
17311         
17312         var setCellClass = function(cal, cell){
17313             cell.row = 0;
17314             cell.events = [];
17315             cell.more = [];
17316             //Roo.log('set Cell Class');
17317             cell.title = "";
17318             var t = d.getTime();
17319             
17320             //Roo.log(d);
17321             
17322             cell.dateValue = t;
17323             if(t == today){
17324                 cell.className += " fc-today";
17325                 cell.className += " fc-state-highlight";
17326                 cell.title = cal.todayText;
17327             }
17328             if(t == sel){
17329                 // disable highlight in other month..
17330                 //cell.className += " fc-state-highlight";
17331                 
17332             }
17333             // disabling
17334             if(t < min) {
17335                 cell.className = " fc-state-disabled";
17336                 cell.title = cal.minText;
17337                 return;
17338             }
17339             if(t > max) {
17340                 cell.className = " fc-state-disabled";
17341                 cell.title = cal.maxText;
17342                 return;
17343             }
17344             if(ddays){
17345                 if(ddays.indexOf(d.getDay()) != -1){
17346                     cell.title = ddaysText;
17347                     cell.className = " fc-state-disabled";
17348                 }
17349             }
17350             if(ddMatch && format){
17351                 var fvalue = d.dateFormat(format);
17352                 if(ddMatch.test(fvalue)){
17353                     cell.title = ddText.replace("%0", fvalue);
17354                     cell.className = " fc-state-disabled";
17355                 }
17356             }
17357             
17358             if (!cell.initialClassName) {
17359                 cell.initialClassName = cell.dom.className;
17360             }
17361             
17362             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17363         };
17364
17365         var i = 0;
17366         
17367         for(; i < startingPos; i++) {
17368             textEls[i].innerHTML = (++prevStart);
17369             d.setDate(d.getDate()+1);
17370             
17371             cells[i].className = "fc-past fc-other-month";
17372             setCellClass(this, cells[i]);
17373         }
17374         
17375         var intDay = 0;
17376         
17377         for(; i < days; i++){
17378             intDay = i - startingPos + 1;
17379             textEls[i].innerHTML = (intDay);
17380             d.setDate(d.getDate()+1);
17381             
17382             cells[i].className = ''; // "x-date-active";
17383             setCellClass(this, cells[i]);
17384         }
17385         var extraDays = 0;
17386         
17387         for(; i < 42; i++) {
17388             textEls[i].innerHTML = (++extraDays);
17389             d.setDate(d.getDate()+1);
17390             
17391             cells[i].className = "fc-future fc-other-month";
17392             setCellClass(this, cells[i]);
17393         }
17394         
17395         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17396         
17397         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17398         
17399         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17400         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17401         
17402         if(totalRows != 6){
17403             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17404             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17405         }
17406         
17407         this.fireEvent('monthchange', this, date);
17408         
17409         
17410         /*
17411         if(!this.internalRender){
17412             var main = this.el.dom.firstChild;
17413             var w = main.offsetWidth;
17414             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17415             Roo.fly(main).setWidth(w);
17416             this.internalRender = true;
17417             // opera does not respect the auto grow header center column
17418             // then, after it gets a width opera refuses to recalculate
17419             // without a second pass
17420             if(Roo.isOpera && !this.secondPass){
17421                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17422                 this.secondPass = true;
17423                 this.update.defer(10, this, [date]);
17424             }
17425         }
17426         */
17427         
17428     },
17429     
17430     findCell : function(dt) {
17431         dt = dt.clearTime().getTime();
17432         var ret = false;
17433         this.cells.each(function(c){
17434             //Roo.log("check " +c.dateValue + '?=' + dt);
17435             if(c.dateValue == dt){
17436                 ret = c;
17437                 return false;
17438             }
17439             return true;
17440         });
17441         
17442         return ret;
17443     },
17444     
17445     findCells : function(ev) {
17446         var s = ev.start.clone().clearTime().getTime();
17447        // Roo.log(s);
17448         var e= ev.end.clone().clearTime().getTime();
17449        // Roo.log(e);
17450         var ret = [];
17451         this.cells.each(function(c){
17452              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17453             
17454             if(c.dateValue > e){
17455                 return ;
17456             }
17457             if(c.dateValue < s){
17458                 return ;
17459             }
17460             ret.push(c);
17461         });
17462         
17463         return ret;    
17464     },
17465     
17466 //    findBestRow: function(cells)
17467 //    {
17468 //        var ret = 0;
17469 //        
17470 //        for (var i =0 ; i < cells.length;i++) {
17471 //            ret  = Math.max(cells[i].rows || 0,ret);
17472 //        }
17473 //        return ret;
17474 //        
17475 //    },
17476     
17477     
17478     addItem : function(ev)
17479     {
17480         // look for vertical location slot in
17481         var cells = this.findCells(ev);
17482         
17483 //        ev.row = this.findBestRow(cells);
17484         
17485         // work out the location.
17486         
17487         var crow = false;
17488         var rows = [];
17489         for(var i =0; i < cells.length; i++) {
17490             
17491             cells[i].row = cells[0].row;
17492             
17493             if(i == 0){
17494                 cells[i].row = cells[i].row + 1;
17495             }
17496             
17497             if (!crow) {
17498                 crow = {
17499                     start : cells[i],
17500                     end :  cells[i]
17501                 };
17502                 continue;
17503             }
17504             if (crow.start.getY() == cells[i].getY()) {
17505                 // on same row.
17506                 crow.end = cells[i];
17507                 continue;
17508             }
17509             // different row.
17510             rows.push(crow);
17511             crow = {
17512                 start: cells[i],
17513                 end : cells[i]
17514             };
17515             
17516         }
17517         
17518         rows.push(crow);
17519         ev.els = [];
17520         ev.rows = rows;
17521         ev.cells = cells;
17522         
17523         cells[0].events.push(ev);
17524         
17525         this.calevents.push(ev);
17526     },
17527     
17528     clearEvents: function() {
17529         
17530         if(!this.calevents){
17531             return;
17532         }
17533         
17534         Roo.each(this.cells.elements, function(c){
17535             c.row = 0;
17536             c.events = [];
17537             c.more = [];
17538         });
17539         
17540         Roo.each(this.calevents, function(e) {
17541             Roo.each(e.els, function(el) {
17542                 el.un('mouseenter' ,this.onEventEnter, this);
17543                 el.un('mouseleave' ,this.onEventLeave, this);
17544                 el.remove();
17545             },this);
17546         },this);
17547         
17548         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17549             e.remove();
17550         });
17551         
17552     },
17553     
17554     renderEvents: function()
17555     {   
17556         var _this = this;
17557         
17558         this.cells.each(function(c) {
17559             
17560             if(c.row < 5){
17561                 return;
17562             }
17563             
17564             var ev = c.events;
17565             
17566             var r = 4;
17567             if(c.row != c.events.length){
17568                 r = 4 - (4 - (c.row - c.events.length));
17569             }
17570             
17571             c.events = ev.slice(0, r);
17572             c.more = ev.slice(r);
17573             
17574             if(c.more.length && c.more.length == 1){
17575                 c.events.push(c.more.pop());
17576             }
17577             
17578             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17579             
17580         });
17581             
17582         this.cells.each(function(c) {
17583             
17584             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17585             
17586             
17587             for (var e = 0; e < c.events.length; e++){
17588                 var ev = c.events[e];
17589                 var rows = ev.rows;
17590                 
17591                 for(var i = 0; i < rows.length; i++) {
17592                 
17593                     // how many rows should it span..
17594
17595                     var  cfg = {
17596                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17597                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17598
17599                         unselectable : "on",
17600                         cn : [
17601                             {
17602                                 cls: 'fc-event-inner',
17603                                 cn : [
17604     //                                {
17605     //                                  tag:'span',
17606     //                                  cls: 'fc-event-time',
17607     //                                  html : cells.length > 1 ? '' : ev.time
17608     //                                },
17609                                     {
17610                                       tag:'span',
17611                                       cls: 'fc-event-title',
17612                                       html : String.format('{0}', ev.title)
17613                                     }
17614
17615
17616                                 ]
17617                             },
17618                             {
17619                                 cls: 'ui-resizable-handle ui-resizable-e',
17620                                 html : '&nbsp;&nbsp;&nbsp'
17621                             }
17622
17623                         ]
17624                     };
17625
17626                     if (i == 0) {
17627                         cfg.cls += ' fc-event-start';
17628                     }
17629                     if ((i+1) == rows.length) {
17630                         cfg.cls += ' fc-event-end';
17631                     }
17632
17633                     var ctr = _this.el.select('.fc-event-container',true).first();
17634                     var cg = ctr.createChild(cfg);
17635
17636                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17637                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17638
17639                     var r = (c.more.length) ? 1 : 0;
17640                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17641                     cg.setWidth(ebox.right - sbox.x -2);
17642
17643                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17644                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17645                     cg.on('click', _this.onEventClick, _this, ev);
17646
17647                     ev.els.push(cg);
17648                     
17649                 }
17650                 
17651             }
17652             
17653             
17654             if(c.more.length){
17655                 var  cfg = {
17656                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17657                     style : 'position: absolute',
17658                     unselectable : "on",
17659                     cn : [
17660                         {
17661                             cls: 'fc-event-inner',
17662                             cn : [
17663                                 {
17664                                   tag:'span',
17665                                   cls: 'fc-event-title',
17666                                   html : 'More'
17667                                 }
17668
17669
17670                             ]
17671                         },
17672                         {
17673                             cls: 'ui-resizable-handle ui-resizable-e',
17674                             html : '&nbsp;&nbsp;&nbsp'
17675                         }
17676
17677                     ]
17678                 };
17679
17680                 var ctr = _this.el.select('.fc-event-container',true).first();
17681                 var cg = ctr.createChild(cfg);
17682
17683                 var sbox = c.select('.fc-day-content',true).first().getBox();
17684                 var ebox = c.select('.fc-day-content',true).first().getBox();
17685                 //Roo.log(cg);
17686                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17687                 cg.setWidth(ebox.right - sbox.x -2);
17688
17689                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17690                 
17691             }
17692             
17693         });
17694         
17695         
17696         
17697     },
17698     
17699     onEventEnter: function (e, el,event,d) {
17700         this.fireEvent('evententer', this, el, event);
17701     },
17702     
17703     onEventLeave: function (e, el,event,d) {
17704         this.fireEvent('eventleave', this, el, event);
17705     },
17706     
17707     onEventClick: function (e, el,event,d) {
17708         this.fireEvent('eventclick', this, el, event);
17709     },
17710     
17711     onMonthChange: function () {
17712         this.store.load();
17713     },
17714     
17715     onMoreEventClick: function(e, el, more)
17716     {
17717         var _this = this;
17718         
17719         this.calpopover.placement = 'right';
17720         this.calpopover.setTitle('More');
17721         
17722         this.calpopover.setContent('');
17723         
17724         var ctr = this.calpopover.el.select('.popover-content', true).first();
17725         
17726         Roo.each(more, function(m){
17727             var cfg = {
17728                 cls : 'fc-event-hori fc-event-draggable',
17729                 html : m.title
17730             };
17731             var cg = ctr.createChild(cfg);
17732             
17733             cg.on('click', _this.onEventClick, _this, m);
17734         });
17735         
17736         this.calpopover.show(el);
17737         
17738         
17739     },
17740     
17741     onLoad: function () 
17742     {   
17743         this.calevents = [];
17744         var cal = this;
17745         
17746         if(this.store.getCount() > 0){
17747             this.store.data.each(function(d){
17748                cal.addItem({
17749                     id : d.data.id,
17750                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17751                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17752                     time : d.data.start_time,
17753                     title : d.data.title,
17754                     description : d.data.description,
17755                     venue : d.data.venue
17756                 });
17757             });
17758         }
17759         
17760         this.renderEvents();
17761         
17762         if(this.calevents.length && this.loadMask){
17763             this.maskEl.hide();
17764         }
17765     },
17766     
17767     onBeforeLoad: function()
17768     {
17769         this.clearEvents();
17770         if(this.loadMask){
17771             this.maskEl.show();
17772         }
17773     }
17774 });
17775
17776  
17777  /*
17778  * - LGPL
17779  *
17780  * element
17781  * 
17782  */
17783
17784 /**
17785  * @class Roo.bootstrap.Popover
17786  * @extends Roo.bootstrap.Component
17787  * Bootstrap Popover class
17788  * @cfg {String} html contents of the popover   (or false to use children..)
17789  * @cfg {String} title of popover (or false to hide)
17790  * @cfg {String} placement how it is placed
17791  * @cfg {String} trigger click || hover (or false to trigger manually)
17792  * @cfg {String} over what (parent or false to trigger manually.)
17793  * @cfg {Number} delay - delay before showing
17794  
17795  * @constructor
17796  * Create a new Popover
17797  * @param {Object} config The config object
17798  */
17799
17800 Roo.bootstrap.Popover = function(config){
17801     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17802     
17803     this.addEvents({
17804         // raw events
17805          /**
17806          * @event show
17807          * After the popover show
17808          * 
17809          * @param {Roo.bootstrap.Popover} this
17810          */
17811         "show" : true,
17812         /**
17813          * @event hide
17814          * After the popover hide
17815          * 
17816          * @param {Roo.bootstrap.Popover} this
17817          */
17818         "hide" : true
17819     });
17820 };
17821
17822 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17823     
17824     title: 'Fill in a title',
17825     html: false,
17826     
17827     placement : 'right',
17828     trigger : 'hover', // hover
17829     
17830     delay : 0,
17831     
17832     over: 'parent',
17833     
17834     can_build_overlaid : false,
17835     
17836     getChildContainer : function()
17837     {
17838         return this.el.select('.popover-content',true).first();
17839     },
17840     
17841     getAutoCreate : function(){
17842          
17843         var cfg = {
17844            cls : 'popover roo-dynamic',
17845            style: 'display:block',
17846            cn : [
17847                 {
17848                     cls : 'arrow'
17849                 },
17850                 {
17851                     cls : 'popover-inner',
17852                     cn : [
17853                         {
17854                             tag: 'h3',
17855                             cls: 'popover-title popover-header',
17856                             html : this.title
17857                         },
17858                         {
17859                             cls : 'popover-content popover-body',
17860                             html : this.html
17861                         }
17862                     ]
17863                     
17864                 }
17865            ]
17866         };
17867         
17868         return cfg;
17869     },
17870     setTitle: function(str)
17871     {
17872         this.title = str;
17873         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17874     },
17875     setContent: function(str)
17876     {
17877         this.html = str;
17878         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17879     },
17880     // as it get's added to the bottom of the page.
17881     onRender : function(ct, position)
17882     {
17883         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17884         if(!this.el){
17885             var cfg = Roo.apply({},  this.getAutoCreate());
17886             cfg.id = Roo.id();
17887             
17888             if (this.cls) {
17889                 cfg.cls += ' ' + this.cls;
17890             }
17891             if (this.style) {
17892                 cfg.style = this.style;
17893             }
17894             //Roo.log("adding to ");
17895             this.el = Roo.get(document.body).createChild(cfg, position);
17896 //            Roo.log(this.el);
17897         }
17898         this.initEvents();
17899     },
17900     
17901     initEvents : function()
17902     {
17903         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17904         this.el.enableDisplayMode('block');
17905         this.el.hide();
17906         if (this.over === false) {
17907             return; 
17908         }
17909         if (this.triggers === false) {
17910             return;
17911         }
17912         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17913         var triggers = this.trigger ? this.trigger.split(' ') : [];
17914         Roo.each(triggers, function(trigger) {
17915         
17916             if (trigger == 'click') {
17917                 on_el.on('click', this.toggle, this);
17918             } else if (trigger != 'manual') {
17919                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17920                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17921       
17922                 on_el.on(eventIn  ,this.enter, this);
17923                 on_el.on(eventOut, this.leave, this);
17924             }
17925         }, this);
17926         
17927     },
17928     
17929     
17930     // private
17931     timeout : null,
17932     hoverState : null,
17933     
17934     toggle : function () {
17935         this.hoverState == 'in' ? this.leave() : this.enter();
17936     },
17937     
17938     enter : function () {
17939         
17940         clearTimeout(this.timeout);
17941     
17942         this.hoverState = 'in';
17943     
17944         if (!this.delay || !this.delay.show) {
17945             this.show();
17946             return;
17947         }
17948         var _t = this;
17949         this.timeout = setTimeout(function () {
17950             if (_t.hoverState == 'in') {
17951                 _t.show();
17952             }
17953         }, this.delay.show)
17954     },
17955     
17956     leave : function() {
17957         clearTimeout(this.timeout);
17958     
17959         this.hoverState = 'out';
17960     
17961         if (!this.delay || !this.delay.hide) {
17962             this.hide();
17963             return;
17964         }
17965         var _t = this;
17966         this.timeout = setTimeout(function () {
17967             if (_t.hoverState == 'out') {
17968                 _t.hide();
17969             }
17970         }, this.delay.hide)
17971     },
17972     
17973     show : function (on_el)
17974     {
17975         if (!on_el) {
17976             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17977         }
17978         
17979         // set content.
17980         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17981         if (this.html !== false) {
17982             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17983         }
17984         this.el.removeClass([
17985             'fade','top','bottom', 'left', 'right','in',
17986             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17987         ]);
17988         if (!this.title.length) {
17989             this.el.select('.popover-title',true).hide();
17990         }
17991         
17992         var placement = typeof this.placement == 'function' ?
17993             this.placement.call(this, this.el, on_el) :
17994             this.placement;
17995             
17996         var autoToken = /\s?auto?\s?/i;
17997         var autoPlace = autoToken.test(placement);
17998         if (autoPlace) {
17999             placement = placement.replace(autoToken, '') || 'top';
18000         }
18001         
18002         //this.el.detach()
18003         //this.el.setXY([0,0]);
18004         this.el.show();
18005         this.el.dom.style.display='block';
18006         this.el.addClass(placement);
18007         
18008         //this.el.appendTo(on_el);
18009         
18010         var p = this.getPosition();
18011         var box = this.el.getBox();
18012         
18013         if (autoPlace) {
18014             // fixme..
18015         }
18016         var align = Roo.bootstrap.Popover.alignment[placement];
18017         
18018 //        Roo.log(align);
18019         this.el.alignTo(on_el, align[0],align[1]);
18020         //var arrow = this.el.select('.arrow',true).first();
18021         //arrow.set(align[2], 
18022         
18023         this.el.addClass('in');
18024         
18025         
18026         if (this.el.hasClass('fade')) {
18027             // fade it?
18028         }
18029         
18030         this.hoverState = 'in';
18031         
18032         this.fireEvent('show', this);
18033         
18034     },
18035     hide : function()
18036     {
18037         this.el.setXY([0,0]);
18038         this.el.removeClass('in');
18039         this.el.hide();
18040         this.hoverState = null;
18041         
18042         this.fireEvent('hide', this);
18043     }
18044     
18045 });
18046
18047 Roo.bootstrap.Popover.alignment = {
18048     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18049     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18050     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18051     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18052 };
18053
18054  /*
18055  * - LGPL
18056  *
18057  * Progress
18058  * 
18059  */
18060
18061 /**
18062  * @class Roo.bootstrap.Progress
18063  * @extends Roo.bootstrap.Component
18064  * Bootstrap Progress class
18065  * @cfg {Boolean} striped striped of the progress bar
18066  * @cfg {Boolean} active animated of the progress bar
18067  * 
18068  * 
18069  * @constructor
18070  * Create a new Progress
18071  * @param {Object} config The config object
18072  */
18073
18074 Roo.bootstrap.Progress = function(config){
18075     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18076 };
18077
18078 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18079     
18080     striped : false,
18081     active: false,
18082     
18083     getAutoCreate : function(){
18084         var cfg = {
18085             tag: 'div',
18086             cls: 'progress'
18087         };
18088         
18089         
18090         if(this.striped){
18091             cfg.cls += ' progress-striped';
18092         }
18093       
18094         if(this.active){
18095             cfg.cls += ' active';
18096         }
18097         
18098         
18099         return cfg;
18100     }
18101    
18102 });
18103
18104  
18105
18106  /*
18107  * - LGPL
18108  *
18109  * ProgressBar
18110  * 
18111  */
18112
18113 /**
18114  * @class Roo.bootstrap.ProgressBar
18115  * @extends Roo.bootstrap.Component
18116  * Bootstrap ProgressBar class
18117  * @cfg {Number} aria_valuenow aria-value now
18118  * @cfg {Number} aria_valuemin aria-value min
18119  * @cfg {Number} aria_valuemax aria-value max
18120  * @cfg {String} label label for the progress bar
18121  * @cfg {String} panel (success | info | warning | danger )
18122  * @cfg {String} role role of the progress bar
18123  * @cfg {String} sr_only text
18124  * 
18125  * 
18126  * @constructor
18127  * Create a new ProgressBar
18128  * @param {Object} config The config object
18129  */
18130
18131 Roo.bootstrap.ProgressBar = function(config){
18132     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18133 };
18134
18135 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18136     
18137     aria_valuenow : 0,
18138     aria_valuemin : 0,
18139     aria_valuemax : 100,
18140     label : false,
18141     panel : false,
18142     role : false,
18143     sr_only: false,
18144     
18145     getAutoCreate : function()
18146     {
18147         
18148         var cfg = {
18149             tag: 'div',
18150             cls: 'progress-bar',
18151             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18152         };
18153         
18154         if(this.sr_only){
18155             cfg.cn = {
18156                 tag: 'span',
18157                 cls: 'sr-only',
18158                 html: this.sr_only
18159             }
18160         }
18161         
18162         if(this.role){
18163             cfg.role = this.role;
18164         }
18165         
18166         if(this.aria_valuenow){
18167             cfg['aria-valuenow'] = this.aria_valuenow;
18168         }
18169         
18170         if(this.aria_valuemin){
18171             cfg['aria-valuemin'] = this.aria_valuemin;
18172         }
18173         
18174         if(this.aria_valuemax){
18175             cfg['aria-valuemax'] = this.aria_valuemax;
18176         }
18177         
18178         if(this.label && !this.sr_only){
18179             cfg.html = this.label;
18180         }
18181         
18182         if(this.panel){
18183             cfg.cls += ' progress-bar-' + this.panel;
18184         }
18185         
18186         return cfg;
18187     },
18188     
18189     update : function(aria_valuenow)
18190     {
18191         this.aria_valuenow = aria_valuenow;
18192         
18193         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18194     }
18195    
18196 });
18197
18198  
18199
18200  /*
18201  * - LGPL
18202  *
18203  * column
18204  * 
18205  */
18206
18207 /**
18208  * @class Roo.bootstrap.TabGroup
18209  * @extends Roo.bootstrap.Column
18210  * Bootstrap Column class
18211  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18212  * @cfg {Boolean} carousel true to make the group behave like a carousel
18213  * @cfg {Boolean} bullets show bullets for the panels
18214  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18215  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18216  * @cfg {Boolean} showarrow (true|false) show arrow default true
18217  * 
18218  * @constructor
18219  * Create a new TabGroup
18220  * @param {Object} config The config object
18221  */
18222
18223 Roo.bootstrap.TabGroup = function(config){
18224     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18225     if (!this.navId) {
18226         this.navId = Roo.id();
18227     }
18228     this.tabs = [];
18229     Roo.bootstrap.TabGroup.register(this);
18230     
18231 };
18232
18233 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18234     
18235     carousel : false,
18236     transition : false,
18237     bullets : 0,
18238     timer : 0,
18239     autoslide : false,
18240     slideFn : false,
18241     slideOnTouch : false,
18242     showarrow : true,
18243     
18244     getAutoCreate : function()
18245     {
18246         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18247         
18248         cfg.cls += ' tab-content';
18249         
18250         if (this.carousel) {
18251             cfg.cls += ' carousel slide';
18252             
18253             cfg.cn = [{
18254                cls : 'carousel-inner',
18255                cn : []
18256             }];
18257         
18258             if(this.bullets  && !Roo.isTouch){
18259                 
18260                 var bullets = {
18261                     cls : 'carousel-bullets',
18262                     cn : []
18263                 };
18264                
18265                 if(this.bullets_cls){
18266                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18267                 }
18268                 
18269                 bullets.cn.push({
18270                     cls : 'clear'
18271                 });
18272                 
18273                 cfg.cn[0].cn.push(bullets);
18274             }
18275             
18276             if(this.showarrow){
18277                 cfg.cn[0].cn.push({
18278                     tag : 'div',
18279                     class : 'carousel-arrow',
18280                     cn : [
18281                         {
18282                             tag : 'div',
18283                             class : 'carousel-prev',
18284                             cn : [
18285                                 {
18286                                     tag : 'i',
18287                                     class : 'fa fa-chevron-left'
18288                                 }
18289                             ]
18290                         },
18291                         {
18292                             tag : 'div',
18293                             class : 'carousel-next',
18294                             cn : [
18295                                 {
18296                                     tag : 'i',
18297                                     class : 'fa fa-chevron-right'
18298                                 }
18299                             ]
18300                         }
18301                     ]
18302                 });
18303             }
18304             
18305         }
18306         
18307         return cfg;
18308     },
18309     
18310     initEvents:  function()
18311     {
18312 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18313 //            this.el.on("touchstart", this.onTouchStart, this);
18314 //        }
18315         
18316         if(this.autoslide){
18317             var _this = this;
18318             
18319             this.slideFn = window.setInterval(function() {
18320                 _this.showPanelNext();
18321             }, this.timer);
18322         }
18323         
18324         if(this.showarrow){
18325             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18326             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18327         }
18328         
18329         
18330     },
18331     
18332 //    onTouchStart : function(e, el, o)
18333 //    {
18334 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18335 //            return;
18336 //        }
18337 //        
18338 //        this.showPanelNext();
18339 //    },
18340     
18341     
18342     getChildContainer : function()
18343     {
18344         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18345     },
18346     
18347     /**
18348     * register a Navigation item
18349     * @param {Roo.bootstrap.NavItem} the navitem to add
18350     */
18351     register : function(item)
18352     {
18353         this.tabs.push( item);
18354         item.navId = this.navId; // not really needed..
18355         this.addBullet();
18356     
18357     },
18358     
18359     getActivePanel : function()
18360     {
18361         var r = false;
18362         Roo.each(this.tabs, function(t) {
18363             if (t.active) {
18364                 r = t;
18365                 return false;
18366             }
18367             return null;
18368         });
18369         return r;
18370         
18371     },
18372     getPanelByName : function(n)
18373     {
18374         var r = false;
18375         Roo.each(this.tabs, function(t) {
18376             if (t.tabId == n) {
18377                 r = t;
18378                 return false;
18379             }
18380             return null;
18381         });
18382         return r;
18383     },
18384     indexOfPanel : function(p)
18385     {
18386         var r = false;
18387         Roo.each(this.tabs, function(t,i) {
18388             if (t.tabId == p.tabId) {
18389                 r = i;
18390                 return false;
18391             }
18392             return null;
18393         });
18394         return r;
18395     },
18396     /**
18397      * show a specific panel
18398      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18399      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18400      */
18401     showPanel : function (pan)
18402     {
18403         if(this.transition || typeof(pan) == 'undefined'){
18404             Roo.log("waiting for the transitionend");
18405             return false;
18406         }
18407         
18408         if (typeof(pan) == 'number') {
18409             pan = this.tabs[pan];
18410         }
18411         
18412         if (typeof(pan) == 'string') {
18413             pan = this.getPanelByName(pan);
18414         }
18415         
18416         var cur = this.getActivePanel();
18417         
18418         if(!pan || !cur){
18419             Roo.log('pan or acitve pan is undefined');
18420             return false;
18421         }
18422         
18423         if (pan.tabId == this.getActivePanel().tabId) {
18424             return true;
18425         }
18426         
18427         if (false === cur.fireEvent('beforedeactivate')) {
18428             return false;
18429         }
18430         
18431         if(this.bullets > 0 && !Roo.isTouch){
18432             this.setActiveBullet(this.indexOfPanel(pan));
18433         }
18434         
18435         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18436             
18437             //class="carousel-item carousel-item-next carousel-item-left"
18438             
18439             this.transition = true;
18440             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18441             var lr = dir == 'next' ? 'left' : 'right';
18442             pan.el.addClass(dir); // or prev
18443             pan.el.addClass('carousel-item-' + dir); // or prev
18444             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18445             cur.el.addClass(lr); // or right
18446             pan.el.addClass(lr);
18447             cur.el.addClass('carousel-item-' +lr); // or right
18448             pan.el.addClass('carousel-item-' +lr);
18449             
18450             
18451             var _this = this;
18452             cur.el.on('transitionend', function() {
18453                 Roo.log("trans end?");
18454                 
18455                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18456                 pan.setActive(true);
18457                 
18458                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18459                 cur.setActive(false);
18460                 
18461                 _this.transition = false;
18462                 
18463             }, this, { single:  true } );
18464             
18465             return true;
18466         }
18467         
18468         cur.setActive(false);
18469         pan.setActive(true);
18470         
18471         return true;
18472         
18473     },
18474     showPanelNext : function()
18475     {
18476         var i = this.indexOfPanel(this.getActivePanel());
18477         
18478         if (i >= this.tabs.length - 1 && !this.autoslide) {
18479             return;
18480         }
18481         
18482         if (i >= this.tabs.length - 1 && this.autoslide) {
18483             i = -1;
18484         }
18485         
18486         this.showPanel(this.tabs[i+1]);
18487     },
18488     
18489     showPanelPrev : function()
18490     {
18491         var i = this.indexOfPanel(this.getActivePanel());
18492         
18493         if (i  < 1 && !this.autoslide) {
18494             return;
18495         }
18496         
18497         if (i < 1 && this.autoslide) {
18498             i = this.tabs.length;
18499         }
18500         
18501         this.showPanel(this.tabs[i-1]);
18502     },
18503     
18504     
18505     addBullet: function()
18506     {
18507         if(!this.bullets || Roo.isTouch){
18508             return;
18509         }
18510         var ctr = this.el.select('.carousel-bullets',true).first();
18511         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18512         var bullet = ctr.createChild({
18513             cls : 'bullet bullet-' + i
18514         },ctr.dom.lastChild);
18515         
18516         
18517         var _this = this;
18518         
18519         bullet.on('click', (function(e, el, o, ii, t){
18520
18521             e.preventDefault();
18522
18523             this.showPanel(ii);
18524
18525             if(this.autoslide && this.slideFn){
18526                 clearInterval(this.slideFn);
18527                 this.slideFn = window.setInterval(function() {
18528                     _this.showPanelNext();
18529                 }, this.timer);
18530             }
18531
18532         }).createDelegate(this, [i, bullet], true));
18533                 
18534         
18535     },
18536      
18537     setActiveBullet : function(i)
18538     {
18539         if(Roo.isTouch){
18540             return;
18541         }
18542         
18543         Roo.each(this.el.select('.bullet', true).elements, function(el){
18544             el.removeClass('selected');
18545         });
18546
18547         var bullet = this.el.select('.bullet-' + i, true).first();
18548         
18549         if(!bullet){
18550             return;
18551         }
18552         
18553         bullet.addClass('selected');
18554     }
18555     
18556     
18557   
18558 });
18559
18560  
18561
18562  
18563  
18564 Roo.apply(Roo.bootstrap.TabGroup, {
18565     
18566     groups: {},
18567      /**
18568     * register a Navigation Group
18569     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18570     */
18571     register : function(navgrp)
18572     {
18573         this.groups[navgrp.navId] = navgrp;
18574         
18575     },
18576     /**
18577     * fetch a Navigation Group based on the navigation ID
18578     * if one does not exist , it will get created.
18579     * @param {string} the navgroup to add
18580     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18581     */
18582     get: function(navId) {
18583         if (typeof(this.groups[navId]) == 'undefined') {
18584             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18585         }
18586         return this.groups[navId] ;
18587     }
18588     
18589     
18590     
18591 });
18592
18593  /*
18594  * - LGPL
18595  *
18596  * TabPanel
18597  * 
18598  */
18599
18600 /**
18601  * @class Roo.bootstrap.TabPanel
18602  * @extends Roo.bootstrap.Component
18603  * Bootstrap TabPanel class
18604  * @cfg {Boolean} active panel active
18605  * @cfg {String} html panel content
18606  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18607  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18608  * @cfg {String} href click to link..
18609  * 
18610  * 
18611  * @constructor
18612  * Create a new TabPanel
18613  * @param {Object} config The config object
18614  */
18615
18616 Roo.bootstrap.TabPanel = function(config){
18617     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18618     this.addEvents({
18619         /**
18620              * @event changed
18621              * Fires when the active status changes
18622              * @param {Roo.bootstrap.TabPanel} this
18623              * @param {Boolean} state the new state
18624             
18625          */
18626         'changed': true,
18627         /**
18628              * @event beforedeactivate
18629              * Fires before a tab is de-activated - can be used to do validation on a form.
18630              * @param {Roo.bootstrap.TabPanel} this
18631              * @return {Boolean} false if there is an error
18632             
18633          */
18634         'beforedeactivate': true
18635      });
18636     
18637     this.tabId = this.tabId || Roo.id();
18638   
18639 };
18640
18641 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18642     
18643     active: false,
18644     html: false,
18645     tabId: false,
18646     navId : false,
18647     href : '',
18648     
18649     getAutoCreate : function(){
18650         
18651         
18652         var cfg = {
18653             tag: 'div',
18654             // item is needed for carousel - not sure if it has any effect otherwise
18655             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18656             html: this.html || ''
18657         };
18658         
18659         if(this.active){
18660             cfg.cls += ' active';
18661         }
18662         
18663         if(this.tabId){
18664             cfg.tabId = this.tabId;
18665         }
18666         
18667         
18668         
18669         return cfg;
18670     },
18671     
18672     initEvents:  function()
18673     {
18674         var p = this.parent();
18675         
18676         this.navId = this.navId || p.navId;
18677         
18678         if (typeof(this.navId) != 'undefined') {
18679             // not really needed.. but just in case.. parent should be a NavGroup.
18680             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18681             
18682             tg.register(this);
18683             
18684             var i = tg.tabs.length - 1;
18685             
18686             if(this.active && tg.bullets > 0 && i < tg.bullets){
18687                 tg.setActiveBullet(i);
18688             }
18689         }
18690         
18691         this.el.on('click', this.onClick, this);
18692         
18693         if(Roo.isTouch){
18694             this.el.on("touchstart", this.onTouchStart, this);
18695             this.el.on("touchmove", this.onTouchMove, this);
18696             this.el.on("touchend", this.onTouchEnd, this);
18697         }
18698         
18699     },
18700     
18701     onRender : function(ct, position)
18702     {
18703         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18704     },
18705     
18706     setActive : function(state)
18707     {
18708         Roo.log("panel - set active " + this.tabId + "=" + state);
18709         
18710         this.active = state;
18711         if (!state) {
18712             this.el.removeClass('active');
18713             
18714         } else  if (!this.el.hasClass('active')) {
18715             this.el.addClass('active');
18716         }
18717         
18718         this.fireEvent('changed', this, state);
18719     },
18720     
18721     onClick : function(e)
18722     {
18723         e.preventDefault();
18724         
18725         if(!this.href.length){
18726             return;
18727         }
18728         
18729         window.location.href = this.href;
18730     },
18731     
18732     startX : 0,
18733     startY : 0,
18734     endX : 0,
18735     endY : 0,
18736     swiping : false,
18737     
18738     onTouchStart : function(e)
18739     {
18740         this.swiping = false;
18741         
18742         this.startX = e.browserEvent.touches[0].clientX;
18743         this.startY = e.browserEvent.touches[0].clientY;
18744     },
18745     
18746     onTouchMove : function(e)
18747     {
18748         this.swiping = true;
18749         
18750         this.endX = e.browserEvent.touches[0].clientX;
18751         this.endY = e.browserEvent.touches[0].clientY;
18752     },
18753     
18754     onTouchEnd : function(e)
18755     {
18756         if(!this.swiping){
18757             this.onClick(e);
18758             return;
18759         }
18760         
18761         var tabGroup = this.parent();
18762         
18763         if(this.endX > this.startX){ // swiping right
18764             tabGroup.showPanelPrev();
18765             return;
18766         }
18767         
18768         if(this.startX > this.endX){ // swiping left
18769             tabGroup.showPanelNext();
18770             return;
18771         }
18772     }
18773     
18774     
18775 });
18776  
18777
18778  
18779
18780  /*
18781  * - LGPL
18782  *
18783  * DateField
18784  * 
18785  */
18786
18787 /**
18788  * @class Roo.bootstrap.DateField
18789  * @extends Roo.bootstrap.Input
18790  * Bootstrap DateField class
18791  * @cfg {Number} weekStart default 0
18792  * @cfg {String} viewMode default empty, (months|years)
18793  * @cfg {String} minViewMode default empty, (months|years)
18794  * @cfg {Number} startDate default -Infinity
18795  * @cfg {Number} endDate default Infinity
18796  * @cfg {Boolean} todayHighlight default false
18797  * @cfg {Boolean} todayBtn default false
18798  * @cfg {Boolean} calendarWeeks default false
18799  * @cfg {Object} daysOfWeekDisabled default empty
18800  * @cfg {Boolean} singleMode default false (true | false)
18801  * 
18802  * @cfg {Boolean} keyboardNavigation default true
18803  * @cfg {String} language default en
18804  * 
18805  * @constructor
18806  * Create a new DateField
18807  * @param {Object} config The config object
18808  */
18809
18810 Roo.bootstrap.DateField = function(config){
18811     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18812      this.addEvents({
18813             /**
18814              * @event show
18815              * Fires when this field show.
18816              * @param {Roo.bootstrap.DateField} this
18817              * @param {Mixed} date The date value
18818              */
18819             show : true,
18820             /**
18821              * @event show
18822              * Fires when this field hide.
18823              * @param {Roo.bootstrap.DateField} this
18824              * @param {Mixed} date The date value
18825              */
18826             hide : true,
18827             /**
18828              * @event select
18829              * Fires when select a date.
18830              * @param {Roo.bootstrap.DateField} this
18831              * @param {Mixed} date The date value
18832              */
18833             select : true,
18834             /**
18835              * @event beforeselect
18836              * Fires when before select a date.
18837              * @param {Roo.bootstrap.DateField} this
18838              * @param {Mixed} date The date value
18839              */
18840             beforeselect : true
18841         });
18842 };
18843
18844 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18845     
18846     /**
18847      * @cfg {String} format
18848      * The default date format string which can be overriden for localization support.  The format must be
18849      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18850      */
18851     format : "m/d/y",
18852     /**
18853      * @cfg {String} altFormats
18854      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18855      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18856      */
18857     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18858     
18859     weekStart : 0,
18860     
18861     viewMode : '',
18862     
18863     minViewMode : '',
18864     
18865     todayHighlight : false,
18866     
18867     todayBtn: false,
18868     
18869     language: 'en',
18870     
18871     keyboardNavigation: true,
18872     
18873     calendarWeeks: false,
18874     
18875     startDate: -Infinity,
18876     
18877     endDate: Infinity,
18878     
18879     daysOfWeekDisabled: [],
18880     
18881     _events: [],
18882     
18883     singleMode : false,
18884     
18885     UTCDate: function()
18886     {
18887         return new Date(Date.UTC.apply(Date, arguments));
18888     },
18889     
18890     UTCToday: function()
18891     {
18892         var today = new Date();
18893         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18894     },
18895     
18896     getDate: function() {
18897             var d = this.getUTCDate();
18898             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18899     },
18900     
18901     getUTCDate: function() {
18902             return this.date;
18903     },
18904     
18905     setDate: function(d) {
18906             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18907     },
18908     
18909     setUTCDate: function(d) {
18910             this.date = d;
18911             this.setValue(this.formatDate(this.date));
18912     },
18913         
18914     onRender: function(ct, position)
18915     {
18916         
18917         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18918         
18919         this.language = this.language || 'en';
18920         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18921         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18922         
18923         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18924         this.format = this.format || 'm/d/y';
18925         this.isInline = false;
18926         this.isInput = true;
18927         this.component = this.el.select('.add-on', true).first() || false;
18928         this.component = (this.component && this.component.length === 0) ? false : this.component;
18929         this.hasInput = this.component && this.inputEl().length;
18930         
18931         if (typeof(this.minViewMode === 'string')) {
18932             switch (this.minViewMode) {
18933                 case 'months':
18934                     this.minViewMode = 1;
18935                     break;
18936                 case 'years':
18937                     this.minViewMode = 2;
18938                     break;
18939                 default:
18940                     this.minViewMode = 0;
18941                     break;
18942             }
18943         }
18944         
18945         if (typeof(this.viewMode === 'string')) {
18946             switch (this.viewMode) {
18947                 case 'months':
18948                     this.viewMode = 1;
18949                     break;
18950                 case 'years':
18951                     this.viewMode = 2;
18952                     break;
18953                 default:
18954                     this.viewMode = 0;
18955                     break;
18956             }
18957         }
18958                 
18959         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18960         
18961 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18962         
18963         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18964         
18965         this.picker().on('mousedown', this.onMousedown, this);
18966         this.picker().on('click', this.onClick, this);
18967         
18968         this.picker().addClass('datepicker-dropdown');
18969         
18970         this.startViewMode = this.viewMode;
18971         
18972         if(this.singleMode){
18973             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18974                 v.setVisibilityMode(Roo.Element.DISPLAY);
18975                 v.hide();
18976             });
18977             
18978             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18979                 v.setStyle('width', '189px');
18980             });
18981         }
18982         
18983         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18984             if(!this.calendarWeeks){
18985                 v.remove();
18986                 return;
18987             }
18988             
18989             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18990             v.attr('colspan', function(i, val){
18991                 return parseInt(val) + 1;
18992             });
18993         });
18994                         
18995         
18996         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18997         
18998         this.setStartDate(this.startDate);
18999         this.setEndDate(this.endDate);
19000         
19001         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
19002         
19003         this.fillDow();
19004         this.fillMonths();
19005         this.update();
19006         this.showMode();
19007         
19008         if(this.isInline) {
19009             this.showPopup();
19010         }
19011     },
19012     
19013     picker : function()
19014     {
19015         return this.pickerEl;
19016 //        return this.el.select('.datepicker', true).first();
19017     },
19018     
19019     fillDow: function()
19020     {
19021         var dowCnt = this.weekStart;
19022         
19023         var dow = {
19024             tag: 'tr',
19025             cn: [
19026                 
19027             ]
19028         };
19029         
19030         if(this.calendarWeeks){
19031             dow.cn.push({
19032                 tag: 'th',
19033                 cls: 'cw',
19034                 html: '&nbsp;'
19035             })
19036         }
19037         
19038         while (dowCnt < this.weekStart + 7) {
19039             dow.cn.push({
19040                 tag: 'th',
19041                 cls: 'dow',
19042                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19043             });
19044         }
19045         
19046         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19047     },
19048     
19049     fillMonths: function()
19050     {    
19051         var i = 0;
19052         var months = this.picker().select('>.datepicker-months td', true).first();
19053         
19054         months.dom.innerHTML = '';
19055         
19056         while (i < 12) {
19057             var month = {
19058                 tag: 'span',
19059                 cls: 'month',
19060                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19061             };
19062             
19063             months.createChild(month);
19064         }
19065         
19066     },
19067     
19068     update: function()
19069     {
19070         this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
19071         
19072         if (this.date < this.startDate) {
19073             this.viewDate = new Date(this.startDate);
19074         } else if (this.date > this.endDate) {
19075             this.viewDate = new Date(this.endDate);
19076         } else {
19077             this.viewDate = new Date(this.date);
19078         }
19079         
19080         this.fill();
19081     },
19082     
19083     fill: function() 
19084     {
19085         var d = new Date(this.viewDate),
19086                 year = d.getUTCFullYear(),
19087                 month = d.getUTCMonth(),
19088                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19089                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19090                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19091                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19092                 currentDate = this.date && this.date.valueOf(),
19093                 today = this.UTCToday();
19094         
19095         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19096         
19097 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19098         
19099 //        this.picker.select('>tfoot th.today').
19100 //                                              .text(dates[this.language].today)
19101 //                                              .toggle(this.todayBtn !== false);
19102     
19103         this.updateNavArrows();
19104         this.fillMonths();
19105                                                 
19106         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19107         
19108         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19109          
19110         prevMonth.setUTCDate(day);
19111         
19112         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19113         
19114         var nextMonth = new Date(prevMonth);
19115         
19116         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19117         
19118         nextMonth = nextMonth.valueOf();
19119         
19120         var fillMonths = false;
19121         
19122         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19123         
19124         while(prevMonth.valueOf() <= nextMonth) {
19125             var clsName = '';
19126             
19127             if (prevMonth.getUTCDay() === this.weekStart) {
19128                 if(fillMonths){
19129                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19130                 }
19131                     
19132                 fillMonths = {
19133                     tag: 'tr',
19134                     cn: []
19135                 };
19136                 
19137                 if(this.calendarWeeks){
19138                     // ISO 8601: First week contains first thursday.
19139                     // ISO also states week starts on Monday, but we can be more abstract here.
19140                     var
19141                     // Start of current week: based on weekstart/current date
19142                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19143                     // Thursday of this week
19144                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19145                     // First Thursday of year, year from thursday
19146                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19147                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19148                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19149                     
19150                     fillMonths.cn.push({
19151                         tag: 'td',
19152                         cls: 'cw',
19153                         html: calWeek
19154                     });
19155                 }
19156             }
19157             
19158             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19159                 clsName += ' old';
19160             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19161                 clsName += ' new';
19162             }
19163             if (this.todayHighlight &&
19164                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19165                 prevMonth.getUTCMonth() == today.getMonth() &&
19166                 prevMonth.getUTCDate() == today.getDate()) {
19167                 clsName += ' today';
19168             }
19169             
19170             if (currentDate && prevMonth.valueOf() === currentDate) {
19171                 clsName += ' active';
19172             }
19173             
19174             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19175                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19176                     clsName += ' disabled';
19177             }
19178             
19179             fillMonths.cn.push({
19180                 tag: 'td',
19181                 cls: 'day ' + clsName,
19182                 html: prevMonth.getDate()
19183             });
19184             
19185             prevMonth.setDate(prevMonth.getDate()+1);
19186         }
19187           
19188         var currentYear = this.date && this.date.getUTCFullYear();
19189         var currentMonth = this.date && this.date.getUTCMonth();
19190         
19191         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19192         
19193         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19194             v.removeClass('active');
19195             
19196             if(currentYear === year && k === currentMonth){
19197                 v.addClass('active');
19198             }
19199             
19200             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19201                 v.addClass('disabled');
19202             }
19203             
19204         });
19205         
19206         
19207         year = parseInt(year/10, 10) * 10;
19208         
19209         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19210         
19211         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19212         
19213         year -= 1;
19214         for (var i = -1; i < 11; i++) {
19215             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19216                 tag: 'span',
19217                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19218                 html: year
19219             });
19220             
19221             year += 1;
19222         }
19223     },
19224     
19225     showMode: function(dir) 
19226     {
19227         if (dir) {
19228             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19229         }
19230         
19231         Roo.each(this.picker().select('>div',true).elements, function(v){
19232             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19233             v.hide();
19234         });
19235         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19236     },
19237     
19238     place: function()
19239     {
19240         if(this.isInline) {
19241             return;
19242         }
19243         
19244         this.picker().removeClass(['bottom', 'top']);
19245         
19246         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19247             /*
19248              * place to the top of element!
19249              *
19250              */
19251             
19252             this.picker().addClass('top');
19253             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19254             
19255             return;
19256         }
19257         
19258         this.picker().addClass('bottom');
19259         
19260         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19261     },
19262     
19263     parseDate : function(value)
19264     {
19265         if(!value || value instanceof Date){
19266             return value;
19267         }
19268         var v = Date.parseDate(value, this.format);
19269         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19270             v = Date.parseDate(value, 'Y-m-d');
19271         }
19272         if(!v && this.altFormats){
19273             if(!this.altFormatsArray){
19274                 this.altFormatsArray = this.altFormats.split("|");
19275             }
19276             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19277                 v = Date.parseDate(value, this.altFormatsArray[i]);
19278             }
19279         }
19280         return v;
19281     },
19282     
19283     formatDate : function(date, fmt)
19284     {   
19285         return (!date || !(date instanceof Date)) ?
19286         date : date.dateFormat(fmt || this.format);
19287     },
19288     
19289     onFocus : function()
19290     {
19291         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19292         this.showPopup();
19293     },
19294     
19295     onBlur : function()
19296     {
19297         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19298         
19299         var d = this.inputEl().getValue();
19300         
19301         this.setValue(d);
19302                 
19303         this.hidePopup();
19304     },
19305     
19306     showPopup : function()
19307     {
19308         this.picker().show();
19309         this.update();
19310         this.place();
19311         
19312         this.fireEvent('showpopup', this, this.date);
19313     },
19314     
19315     hidePopup : function()
19316     {
19317         if(this.isInline) {
19318             return;
19319         }
19320         this.picker().hide();
19321         this.viewMode = this.startViewMode;
19322         this.showMode();
19323         
19324         this.fireEvent('hidepopup', this, this.date);
19325         
19326     },
19327     
19328     onMousedown: function(e)
19329     {
19330         e.stopPropagation();
19331         e.preventDefault();
19332     },
19333     
19334     keyup: function(e)
19335     {
19336         Roo.bootstrap.DateField.superclass.keyup.call(this);
19337         this.update();
19338     },
19339
19340     setValue: function(v)
19341     {
19342         if(this.fireEvent('beforeselect', this, v) !== false){
19343             var d = new Date(this.parseDate(v) ).clearTime();
19344         
19345             if(isNaN(d.getTime())){
19346                 this.date = this.viewDate = '';
19347                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19348                 return;
19349             }
19350
19351             v = this.formatDate(d);
19352
19353             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19354
19355             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19356
19357             this.update();
19358
19359             this.fireEvent('select', this, this.date);
19360         }
19361     },
19362     
19363     getValue: function()
19364     {
19365         return this.formatDate(this.date);
19366     },
19367     
19368     fireKey: function(e)
19369     {
19370         if (!this.picker().isVisible()){
19371             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19372                 this.showPopup();
19373             }
19374             return;
19375         }
19376         
19377         var dateChanged = false,
19378         dir, day, month,
19379         newDate, newViewDate;
19380         
19381         switch(e.keyCode){
19382             case 27: // escape
19383                 this.hidePopup();
19384                 e.preventDefault();
19385                 break;
19386             case 37: // left
19387             case 39: // right
19388                 if (!this.keyboardNavigation) {
19389                     break;
19390                 }
19391                 dir = e.keyCode == 37 ? -1 : 1;
19392                 
19393                 if (e.ctrlKey){
19394                     newDate = this.moveYear(this.date, dir);
19395                     newViewDate = this.moveYear(this.viewDate, dir);
19396                 } else if (e.shiftKey){
19397                     newDate = this.moveMonth(this.date, dir);
19398                     newViewDate = this.moveMonth(this.viewDate, dir);
19399                 } else {
19400                     newDate = new Date(this.date);
19401                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19402                     newViewDate = new Date(this.viewDate);
19403                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19404                 }
19405                 if (this.dateWithinRange(newDate)){
19406                     this.date = newDate;
19407                     this.viewDate = newViewDate;
19408                     this.setValue(this.formatDate(this.date));
19409 //                    this.update();
19410                     e.preventDefault();
19411                     dateChanged = true;
19412                 }
19413                 break;
19414             case 38: // up
19415             case 40: // down
19416                 if (!this.keyboardNavigation) {
19417                     break;
19418                 }
19419                 dir = e.keyCode == 38 ? -1 : 1;
19420                 if (e.ctrlKey){
19421                     newDate = this.moveYear(this.date, dir);
19422                     newViewDate = this.moveYear(this.viewDate, dir);
19423                 } else if (e.shiftKey){
19424                     newDate = this.moveMonth(this.date, dir);
19425                     newViewDate = this.moveMonth(this.viewDate, dir);
19426                 } else {
19427                     newDate = new Date(this.date);
19428                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19429                     newViewDate = new Date(this.viewDate);
19430                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19431                 }
19432                 if (this.dateWithinRange(newDate)){
19433                     this.date = newDate;
19434                     this.viewDate = newViewDate;
19435                     this.setValue(this.formatDate(this.date));
19436 //                    this.update();
19437                     e.preventDefault();
19438                     dateChanged = true;
19439                 }
19440                 break;
19441             case 13: // enter
19442                 this.setValue(this.formatDate(this.date));
19443                 this.hidePopup();
19444                 e.preventDefault();
19445                 break;
19446             case 9: // tab
19447                 this.setValue(this.formatDate(this.date));
19448                 this.hidePopup();
19449                 break;
19450             case 16: // shift
19451             case 17: // ctrl
19452             case 18: // alt
19453                 break;
19454             default :
19455                 this.hidePopup();
19456                 
19457         }
19458     },
19459     
19460     
19461     onClick: function(e) 
19462     {
19463         e.stopPropagation();
19464         e.preventDefault();
19465         
19466         var target = e.getTarget();
19467         
19468         if(target.nodeName.toLowerCase() === 'i'){
19469             target = Roo.get(target).dom.parentNode;
19470         }
19471         
19472         var nodeName = target.nodeName;
19473         var className = target.className;
19474         var html = target.innerHTML;
19475         //Roo.log(nodeName);
19476         
19477         switch(nodeName.toLowerCase()) {
19478             case 'th':
19479                 switch(className) {
19480                     case 'switch':
19481                         this.showMode(1);
19482                         break;
19483                     case 'prev':
19484                     case 'next':
19485                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19486                         switch(this.viewMode){
19487                                 case 0:
19488                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19489                                         break;
19490                                 case 1:
19491                                 case 2:
19492                                         this.viewDate = this.moveYear(this.viewDate, dir);
19493                                         break;
19494                         }
19495                         this.fill();
19496                         break;
19497                     case 'today':
19498                         var date = new Date();
19499                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19500 //                        this.fill()
19501                         this.setValue(this.formatDate(this.date));
19502                         
19503                         this.hidePopup();
19504                         break;
19505                 }
19506                 break;
19507             case 'span':
19508                 if (className.indexOf('disabled') < 0) {
19509                     this.viewDate.setUTCDate(1);
19510                     if (className.indexOf('month') > -1) {
19511                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19512                     } else {
19513                         var year = parseInt(html, 10) || 0;
19514                         this.viewDate.setUTCFullYear(year);
19515                         
19516                     }
19517                     
19518                     if(this.singleMode){
19519                         this.setValue(this.formatDate(this.viewDate));
19520                         this.hidePopup();
19521                         return;
19522                     }
19523                     
19524                     this.showMode(-1);
19525                     this.fill();
19526                 }
19527                 break;
19528                 
19529             case 'td':
19530                 //Roo.log(className);
19531                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19532                     var day = parseInt(html, 10) || 1;
19533                     var year = this.viewDate.getUTCFullYear(),
19534                         month = this.viewDate.getUTCMonth();
19535
19536                     if (className.indexOf('old') > -1) {
19537                         if(month === 0 ){
19538                             month = 11;
19539                             year -= 1;
19540                         }else{
19541                             month -= 1;
19542                         }
19543                     } else if (className.indexOf('new') > -1) {
19544                         if (month == 11) {
19545                             month = 0;
19546                             year += 1;
19547                         } else {
19548                             month += 1;
19549                         }
19550                     }
19551                     //Roo.log([year,month,day]);
19552                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19553                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19554 //                    this.fill();
19555                     //Roo.log(this.formatDate(this.date));
19556                     this.setValue(this.formatDate(this.date));
19557                     this.hidePopup();
19558                 }
19559                 break;
19560         }
19561     },
19562     
19563     setStartDate: function(startDate)
19564     {
19565         this.startDate = startDate || -Infinity;
19566         if (this.startDate !== -Infinity) {
19567             this.startDate = this.parseDate(this.startDate);
19568         }
19569         this.update();
19570         this.updateNavArrows();
19571     },
19572
19573     setEndDate: function(endDate)
19574     {
19575         this.endDate = endDate || Infinity;
19576         if (this.endDate !== Infinity) {
19577             this.endDate = this.parseDate(this.endDate);
19578         }
19579         this.update();
19580         this.updateNavArrows();
19581     },
19582     
19583     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19584     {
19585         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19586         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19587             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19588         }
19589         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19590             return parseInt(d, 10);
19591         });
19592         this.update();
19593         this.updateNavArrows();
19594     },
19595     
19596     updateNavArrows: function() 
19597     {
19598         if(this.singleMode){
19599             return;
19600         }
19601         
19602         var d = new Date(this.viewDate),
19603         year = d.getUTCFullYear(),
19604         month = d.getUTCMonth();
19605         
19606         Roo.each(this.picker().select('.prev', true).elements, function(v){
19607             v.show();
19608             switch (this.viewMode) {
19609                 case 0:
19610
19611                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19612                         v.hide();
19613                     }
19614                     break;
19615                 case 1:
19616                 case 2:
19617                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19618                         v.hide();
19619                     }
19620                     break;
19621             }
19622         });
19623         
19624         Roo.each(this.picker().select('.next', true).elements, function(v){
19625             v.show();
19626             switch (this.viewMode) {
19627                 case 0:
19628
19629                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19630                         v.hide();
19631                     }
19632                     break;
19633                 case 1:
19634                 case 2:
19635                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19636                         v.hide();
19637                     }
19638                     break;
19639             }
19640         })
19641     },
19642     
19643     moveMonth: function(date, dir)
19644     {
19645         if (!dir) {
19646             return date;
19647         }
19648         var new_date = new Date(date.valueOf()),
19649         day = new_date.getUTCDate(),
19650         month = new_date.getUTCMonth(),
19651         mag = Math.abs(dir),
19652         new_month, test;
19653         dir = dir > 0 ? 1 : -1;
19654         if (mag == 1){
19655             test = dir == -1
19656             // If going back one month, make sure month is not current month
19657             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19658             ? function(){
19659                 return new_date.getUTCMonth() == month;
19660             }
19661             // If going forward one month, make sure month is as expected
19662             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19663             : function(){
19664                 return new_date.getUTCMonth() != new_month;
19665             };
19666             new_month = month + dir;
19667             new_date.setUTCMonth(new_month);
19668             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19669             if (new_month < 0 || new_month > 11) {
19670                 new_month = (new_month + 12) % 12;
19671             }
19672         } else {
19673             // For magnitudes >1, move one month at a time...
19674             for (var i=0; i<mag; i++) {
19675                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19676                 new_date = this.moveMonth(new_date, dir);
19677             }
19678             // ...then reset the day, keeping it in the new month
19679             new_month = new_date.getUTCMonth();
19680             new_date.setUTCDate(day);
19681             test = function(){
19682                 return new_month != new_date.getUTCMonth();
19683             };
19684         }
19685         // Common date-resetting loop -- if date is beyond end of month, make it
19686         // end of month
19687         while (test()){
19688             new_date.setUTCDate(--day);
19689             new_date.setUTCMonth(new_month);
19690         }
19691         return new_date;
19692     },
19693
19694     moveYear: function(date, dir)
19695     {
19696         return this.moveMonth(date, dir*12);
19697     },
19698
19699     dateWithinRange: function(date)
19700     {
19701         return date >= this.startDate && date <= this.endDate;
19702     },
19703
19704     
19705     remove: function() 
19706     {
19707         this.picker().remove();
19708     },
19709     
19710     validateValue : function(value)
19711     {
19712         if(this.getVisibilityEl().hasClass('hidden')){
19713             return true;
19714         }
19715         
19716         if(value.length < 1)  {
19717             if(this.allowBlank){
19718                 return true;
19719             }
19720             return false;
19721         }
19722         
19723         if(value.length < this.minLength){
19724             return false;
19725         }
19726         if(value.length > this.maxLength){
19727             return false;
19728         }
19729         if(this.vtype){
19730             var vt = Roo.form.VTypes;
19731             if(!vt[this.vtype](value, this)){
19732                 return false;
19733             }
19734         }
19735         if(typeof this.validator == "function"){
19736             var msg = this.validator(value);
19737             if(msg !== true){
19738                 return false;
19739             }
19740         }
19741         
19742         if(this.regex && !this.regex.test(value)){
19743             return false;
19744         }
19745         
19746         if(typeof(this.parseDate(value)) == 'undefined'){
19747             return false;
19748         }
19749         
19750         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19751             return false;
19752         }      
19753         
19754         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19755             return false;
19756         } 
19757         
19758         
19759         return true;
19760     },
19761     
19762     reset : function()
19763     {
19764         this.date = this.viewDate = '';
19765         
19766         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19767     }
19768    
19769 });
19770
19771 Roo.apply(Roo.bootstrap.DateField,  {
19772     
19773     head : {
19774         tag: 'thead',
19775         cn: [
19776         {
19777             tag: 'tr',
19778             cn: [
19779             {
19780                 tag: 'th',
19781                 cls: 'prev',
19782                 html: '<i class="fa fa-arrow-left"/>'
19783             },
19784             {
19785                 tag: 'th',
19786                 cls: 'switch',
19787                 colspan: '5'
19788             },
19789             {
19790                 tag: 'th',
19791                 cls: 'next',
19792                 html: '<i class="fa fa-arrow-right"/>'
19793             }
19794
19795             ]
19796         }
19797         ]
19798     },
19799     
19800     content : {
19801         tag: 'tbody',
19802         cn: [
19803         {
19804             tag: 'tr',
19805             cn: [
19806             {
19807                 tag: 'td',
19808                 colspan: '7'
19809             }
19810             ]
19811         }
19812         ]
19813     },
19814     
19815     footer : {
19816         tag: 'tfoot',
19817         cn: [
19818         {
19819             tag: 'tr',
19820             cn: [
19821             {
19822                 tag: 'th',
19823                 colspan: '7',
19824                 cls: 'today'
19825             }
19826                     
19827             ]
19828         }
19829         ]
19830     },
19831     
19832     dates:{
19833         en: {
19834             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19835             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19836             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19837             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19838             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19839             today: "Today"
19840         }
19841     },
19842     
19843     modes: [
19844     {
19845         clsName: 'days',
19846         navFnc: 'Month',
19847         navStep: 1
19848     },
19849     {
19850         clsName: 'months',
19851         navFnc: 'FullYear',
19852         navStep: 1
19853     },
19854     {
19855         clsName: 'years',
19856         navFnc: 'FullYear',
19857         navStep: 10
19858     }]
19859 });
19860
19861 Roo.apply(Roo.bootstrap.DateField,  {
19862   
19863     template : {
19864         tag: 'div',
19865         cls: 'datepicker dropdown-menu roo-dynamic',
19866         cn: [
19867         {
19868             tag: 'div',
19869             cls: 'datepicker-days',
19870             cn: [
19871             {
19872                 tag: 'table',
19873                 cls: 'table-condensed',
19874                 cn:[
19875                 Roo.bootstrap.DateField.head,
19876                 {
19877                     tag: 'tbody'
19878                 },
19879                 Roo.bootstrap.DateField.footer
19880                 ]
19881             }
19882             ]
19883         },
19884         {
19885             tag: 'div',
19886             cls: 'datepicker-months',
19887             cn: [
19888             {
19889                 tag: 'table',
19890                 cls: 'table-condensed',
19891                 cn:[
19892                 Roo.bootstrap.DateField.head,
19893                 Roo.bootstrap.DateField.content,
19894                 Roo.bootstrap.DateField.footer
19895                 ]
19896             }
19897             ]
19898         },
19899         {
19900             tag: 'div',
19901             cls: 'datepicker-years',
19902             cn: [
19903             {
19904                 tag: 'table',
19905                 cls: 'table-condensed',
19906                 cn:[
19907                 Roo.bootstrap.DateField.head,
19908                 Roo.bootstrap.DateField.content,
19909                 Roo.bootstrap.DateField.footer
19910                 ]
19911             }
19912             ]
19913         }
19914         ]
19915     }
19916 });
19917
19918  
19919
19920  /*
19921  * - LGPL
19922  *
19923  * TimeField
19924  * 
19925  */
19926
19927 /**
19928  * @class Roo.bootstrap.TimeField
19929  * @extends Roo.bootstrap.Input
19930  * Bootstrap DateField class
19931  * 
19932  * 
19933  * @constructor
19934  * Create a new TimeField
19935  * @param {Object} config The config object
19936  */
19937
19938 Roo.bootstrap.TimeField = function(config){
19939     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19940     this.addEvents({
19941             /**
19942              * @event show
19943              * Fires when this field show.
19944              * @param {Roo.bootstrap.DateField} thisthis
19945              * @param {Mixed} date The date value
19946              */
19947             show : true,
19948             /**
19949              * @event show
19950              * Fires when this field hide.
19951              * @param {Roo.bootstrap.DateField} this
19952              * @param {Mixed} date The date value
19953              */
19954             hide : true,
19955             /**
19956              * @event select
19957              * Fires when select a date.
19958              * @param {Roo.bootstrap.DateField} this
19959              * @param {Mixed} date The date value
19960              */
19961             select : true
19962         });
19963 };
19964
19965 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19966     
19967     /**
19968      * @cfg {String} format
19969      * The default time format string which can be overriden for localization support.  The format must be
19970      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19971      */
19972     format : "H:i",
19973        
19974     onRender: function(ct, position)
19975     {
19976         
19977         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19978                 
19979         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19980         
19981         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19982         
19983         this.pop = this.picker().select('>.datepicker-time',true).first();
19984         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19985         
19986         this.picker().on('mousedown', this.onMousedown, this);
19987         this.picker().on('click', this.onClick, this);
19988         
19989         this.picker().addClass('datepicker-dropdown');
19990     
19991         this.fillTime();
19992         this.update();
19993             
19994         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19995         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19996         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19997         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19998         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19999         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
20000
20001     },
20002     
20003     fireKey: function(e){
20004         if (!this.picker().isVisible()){
20005             if (e.keyCode == 27) { // allow escape to hide and re-show picker
20006                 this.show();
20007             }
20008             return;
20009         }
20010
20011         e.preventDefault();
20012         
20013         switch(e.keyCode){
20014             case 27: // escape
20015                 this.hide();
20016                 break;
20017             case 37: // left
20018             case 39: // right
20019                 this.onTogglePeriod();
20020                 break;
20021             case 38: // up
20022                 this.onIncrementMinutes();
20023                 break;
20024             case 40: // down
20025                 this.onDecrementMinutes();
20026                 break;
20027             case 13: // enter
20028             case 9: // tab
20029                 this.setTime();
20030                 break;
20031         }
20032     },
20033     
20034     onClick: function(e) {
20035         e.stopPropagation();
20036         e.preventDefault();
20037     },
20038     
20039     picker : function()
20040     {
20041         return this.el.select('.datepicker', true).first();
20042     },
20043     
20044     fillTime: function()
20045     {    
20046         var time = this.pop.select('tbody', true).first();
20047         
20048         time.dom.innerHTML = '';
20049         
20050         time.createChild({
20051             tag: 'tr',
20052             cn: [
20053                 {
20054                     tag: 'td',
20055                     cn: [
20056                         {
20057                             tag: 'a',
20058                             href: '#',
20059                             cls: 'btn',
20060                             cn: [
20061                                 {
20062                                     tag: 'span',
20063                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20064                                 }
20065                             ]
20066                         } 
20067                     ]
20068                 },
20069                 {
20070                     tag: 'td',
20071                     cls: 'separator'
20072                 },
20073                 {
20074                     tag: 'td',
20075                     cn: [
20076                         {
20077                             tag: 'a',
20078                             href: '#',
20079                             cls: 'btn',
20080                             cn: [
20081                                 {
20082                                     tag: 'span',
20083                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20084                                 }
20085                             ]
20086                         }
20087                     ]
20088                 },
20089                 {
20090                     tag: 'td',
20091                     cls: 'separator'
20092                 }
20093             ]
20094         });
20095         
20096         time.createChild({
20097             tag: 'tr',
20098             cn: [
20099                 {
20100                     tag: 'td',
20101                     cn: [
20102                         {
20103                             tag: 'span',
20104                             cls: 'timepicker-hour',
20105                             html: '00'
20106                         }  
20107                     ]
20108                 },
20109                 {
20110                     tag: 'td',
20111                     cls: 'separator',
20112                     html: ':'
20113                 },
20114                 {
20115                     tag: 'td',
20116                     cn: [
20117                         {
20118                             tag: 'span',
20119                             cls: 'timepicker-minute',
20120                             html: '00'
20121                         }  
20122                     ]
20123                 },
20124                 {
20125                     tag: 'td',
20126                     cls: 'separator'
20127                 },
20128                 {
20129                     tag: 'td',
20130                     cn: [
20131                         {
20132                             tag: 'button',
20133                             type: 'button',
20134                             cls: 'btn btn-primary period',
20135                             html: 'AM'
20136                             
20137                         }
20138                     ]
20139                 }
20140             ]
20141         });
20142         
20143         time.createChild({
20144             tag: 'tr',
20145             cn: [
20146                 {
20147                     tag: 'td',
20148                     cn: [
20149                         {
20150                             tag: 'a',
20151                             href: '#',
20152                             cls: 'btn',
20153                             cn: [
20154                                 {
20155                                     tag: 'span',
20156                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20157                                 }
20158                             ]
20159                         }
20160                     ]
20161                 },
20162                 {
20163                     tag: 'td',
20164                     cls: 'separator'
20165                 },
20166                 {
20167                     tag: 'td',
20168                     cn: [
20169                         {
20170                             tag: 'a',
20171                             href: '#',
20172                             cls: 'btn',
20173                             cn: [
20174                                 {
20175                                     tag: 'span',
20176                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20177                                 }
20178                             ]
20179                         }
20180                     ]
20181                 },
20182                 {
20183                     tag: 'td',
20184                     cls: 'separator'
20185                 }
20186             ]
20187         });
20188         
20189     },
20190     
20191     update: function()
20192     {
20193         
20194         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20195         
20196         this.fill();
20197     },
20198     
20199     fill: function() 
20200     {
20201         var hours = this.time.getHours();
20202         var minutes = this.time.getMinutes();
20203         var period = 'AM';
20204         
20205         if(hours > 11){
20206             period = 'PM';
20207         }
20208         
20209         if(hours == 0){
20210             hours = 12;
20211         }
20212         
20213         
20214         if(hours > 12){
20215             hours = hours - 12;
20216         }
20217         
20218         if(hours < 10){
20219             hours = '0' + hours;
20220         }
20221         
20222         if(minutes < 10){
20223             minutes = '0' + minutes;
20224         }
20225         
20226         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20227         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20228         this.pop.select('button', true).first().dom.innerHTML = period;
20229         
20230     },
20231     
20232     place: function()
20233     {   
20234         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20235         
20236         var cls = ['bottom'];
20237         
20238         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20239             cls.pop();
20240             cls.push('top');
20241         }
20242         
20243         cls.push('right');
20244         
20245         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20246             cls.pop();
20247             cls.push('left');
20248         }
20249         
20250         this.picker().addClass(cls.join('-'));
20251         
20252         var _this = this;
20253         
20254         Roo.each(cls, function(c){
20255             if(c == 'bottom'){
20256                 _this.picker().setTop(_this.inputEl().getHeight());
20257                 return;
20258             }
20259             if(c == 'top'){
20260                 _this.picker().setTop(0 - _this.picker().getHeight());
20261                 return;
20262             }
20263             
20264             if(c == 'left'){
20265                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20266                 return;
20267             }
20268             if(c == 'right'){
20269                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20270                 return;
20271             }
20272         });
20273         
20274     },
20275   
20276     onFocus : function()
20277     {
20278         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20279         this.show();
20280     },
20281     
20282     onBlur : function()
20283     {
20284         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20285         this.hide();
20286     },
20287     
20288     show : function()
20289     {
20290         this.picker().show();
20291         this.pop.show();
20292         this.update();
20293         this.place();
20294         
20295         this.fireEvent('show', this, this.date);
20296     },
20297     
20298     hide : function()
20299     {
20300         this.picker().hide();
20301         this.pop.hide();
20302         
20303         this.fireEvent('hide', this, this.date);
20304     },
20305     
20306     setTime : function()
20307     {
20308         this.hide();
20309         this.setValue(this.time.format(this.format));
20310         
20311         this.fireEvent('select', this, this.date);
20312         
20313         
20314     },
20315     
20316     onMousedown: function(e){
20317         e.stopPropagation();
20318         e.preventDefault();
20319     },
20320     
20321     onIncrementHours: function()
20322     {
20323         Roo.log('onIncrementHours');
20324         this.time = this.time.add(Date.HOUR, 1);
20325         this.update();
20326         
20327     },
20328     
20329     onDecrementHours: function()
20330     {
20331         Roo.log('onDecrementHours');
20332         this.time = this.time.add(Date.HOUR, -1);
20333         this.update();
20334     },
20335     
20336     onIncrementMinutes: function()
20337     {
20338         Roo.log('onIncrementMinutes');
20339         this.time = this.time.add(Date.MINUTE, 1);
20340         this.update();
20341     },
20342     
20343     onDecrementMinutes: function()
20344     {
20345         Roo.log('onDecrementMinutes');
20346         this.time = this.time.add(Date.MINUTE, -1);
20347         this.update();
20348     },
20349     
20350     onTogglePeriod: function()
20351     {
20352         Roo.log('onTogglePeriod');
20353         this.time = this.time.add(Date.HOUR, 12);
20354         this.update();
20355     }
20356     
20357    
20358 });
20359
20360 Roo.apply(Roo.bootstrap.TimeField,  {
20361     
20362     content : {
20363         tag: 'tbody',
20364         cn: [
20365             {
20366                 tag: 'tr',
20367                 cn: [
20368                 {
20369                     tag: 'td',
20370                     colspan: '7'
20371                 }
20372                 ]
20373             }
20374         ]
20375     },
20376     
20377     footer : {
20378         tag: 'tfoot',
20379         cn: [
20380             {
20381                 tag: 'tr',
20382                 cn: [
20383                 {
20384                     tag: 'th',
20385                     colspan: '7',
20386                     cls: '',
20387                     cn: [
20388                         {
20389                             tag: 'button',
20390                             cls: 'btn btn-info ok',
20391                             html: 'OK'
20392                         }
20393                     ]
20394                 }
20395
20396                 ]
20397             }
20398         ]
20399     }
20400 });
20401
20402 Roo.apply(Roo.bootstrap.TimeField,  {
20403   
20404     template : {
20405         tag: 'div',
20406         cls: 'datepicker dropdown-menu',
20407         cn: [
20408             {
20409                 tag: 'div',
20410                 cls: 'datepicker-time',
20411                 cn: [
20412                 {
20413                     tag: 'table',
20414                     cls: 'table-condensed',
20415                     cn:[
20416                     Roo.bootstrap.TimeField.content,
20417                     Roo.bootstrap.TimeField.footer
20418                     ]
20419                 }
20420                 ]
20421             }
20422         ]
20423     }
20424 });
20425
20426  
20427
20428  /*
20429  * - LGPL
20430  *
20431  * MonthField
20432  * 
20433  */
20434
20435 /**
20436  * @class Roo.bootstrap.MonthField
20437  * @extends Roo.bootstrap.Input
20438  * Bootstrap MonthField class
20439  * 
20440  * @cfg {String} language default en
20441  * 
20442  * @constructor
20443  * Create a new MonthField
20444  * @param {Object} config The config object
20445  */
20446
20447 Roo.bootstrap.MonthField = function(config){
20448     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20449     
20450     this.addEvents({
20451         /**
20452          * @event show
20453          * Fires when this field show.
20454          * @param {Roo.bootstrap.MonthField} this
20455          * @param {Mixed} date The date value
20456          */
20457         show : true,
20458         /**
20459          * @event show
20460          * Fires when this field hide.
20461          * @param {Roo.bootstrap.MonthField} this
20462          * @param {Mixed} date The date value
20463          */
20464         hide : true,
20465         /**
20466          * @event select
20467          * Fires when select a date.
20468          * @param {Roo.bootstrap.MonthField} this
20469          * @param {String} oldvalue The old value
20470          * @param {String} newvalue The new value
20471          */
20472         select : true
20473     });
20474 };
20475
20476 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20477     
20478     onRender: function(ct, position)
20479     {
20480         
20481         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20482         
20483         this.language = this.language || 'en';
20484         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20485         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20486         
20487         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20488         this.isInline = false;
20489         this.isInput = true;
20490         this.component = this.el.select('.add-on', true).first() || false;
20491         this.component = (this.component && this.component.length === 0) ? false : this.component;
20492         this.hasInput = this.component && this.inputEL().length;
20493         
20494         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20495         
20496         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20497         
20498         this.picker().on('mousedown', this.onMousedown, this);
20499         this.picker().on('click', this.onClick, this);
20500         
20501         this.picker().addClass('datepicker-dropdown');
20502         
20503         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20504             v.setStyle('width', '189px');
20505         });
20506         
20507         this.fillMonths();
20508         
20509         this.update();
20510         
20511         if(this.isInline) {
20512             this.show();
20513         }
20514         
20515     },
20516     
20517     setValue: function(v, suppressEvent)
20518     {   
20519         var o = this.getValue();
20520         
20521         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20522         
20523         this.update();
20524
20525         if(suppressEvent !== true){
20526             this.fireEvent('select', this, o, v);
20527         }
20528         
20529     },
20530     
20531     getValue: function()
20532     {
20533         return this.value;
20534     },
20535     
20536     onClick: function(e) 
20537     {
20538         e.stopPropagation();
20539         e.preventDefault();
20540         
20541         var target = e.getTarget();
20542         
20543         if(target.nodeName.toLowerCase() === 'i'){
20544             target = Roo.get(target).dom.parentNode;
20545         }
20546         
20547         var nodeName = target.nodeName;
20548         var className = target.className;
20549         var html = target.innerHTML;
20550         
20551         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20552             return;
20553         }
20554         
20555         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20556         
20557         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20558         
20559         this.hide();
20560                         
20561     },
20562     
20563     picker : function()
20564     {
20565         return this.pickerEl;
20566     },
20567     
20568     fillMonths: function()
20569     {    
20570         var i = 0;
20571         var months = this.picker().select('>.datepicker-months td', true).first();
20572         
20573         months.dom.innerHTML = '';
20574         
20575         while (i < 12) {
20576             var month = {
20577                 tag: 'span',
20578                 cls: 'month',
20579                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20580             };
20581             
20582             months.createChild(month);
20583         }
20584         
20585     },
20586     
20587     update: function()
20588     {
20589         var _this = this;
20590         
20591         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20592             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20593         }
20594         
20595         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20596             e.removeClass('active');
20597             
20598             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20599                 e.addClass('active');
20600             }
20601         })
20602     },
20603     
20604     place: function()
20605     {
20606         if(this.isInline) {
20607             return;
20608         }
20609         
20610         this.picker().removeClass(['bottom', 'top']);
20611         
20612         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20613             /*
20614              * place to the top of element!
20615              *
20616              */
20617             
20618             this.picker().addClass('top');
20619             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20620             
20621             return;
20622         }
20623         
20624         this.picker().addClass('bottom');
20625         
20626         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20627     },
20628     
20629     onFocus : function()
20630     {
20631         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20632         this.show();
20633     },
20634     
20635     onBlur : function()
20636     {
20637         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20638         
20639         var d = this.inputEl().getValue();
20640         
20641         this.setValue(d);
20642                 
20643         this.hide();
20644     },
20645     
20646     show : function()
20647     {
20648         this.picker().show();
20649         this.picker().select('>.datepicker-months', true).first().show();
20650         this.update();
20651         this.place();
20652         
20653         this.fireEvent('show', this, this.date);
20654     },
20655     
20656     hide : function()
20657     {
20658         if(this.isInline) {
20659             return;
20660         }
20661         this.picker().hide();
20662         this.fireEvent('hide', this, this.date);
20663         
20664     },
20665     
20666     onMousedown: function(e)
20667     {
20668         e.stopPropagation();
20669         e.preventDefault();
20670     },
20671     
20672     keyup: function(e)
20673     {
20674         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20675         this.update();
20676     },
20677
20678     fireKey: function(e)
20679     {
20680         if (!this.picker().isVisible()){
20681             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20682                 this.show();
20683             }
20684             return;
20685         }
20686         
20687         var dir;
20688         
20689         switch(e.keyCode){
20690             case 27: // escape
20691                 this.hide();
20692                 e.preventDefault();
20693                 break;
20694             case 37: // left
20695             case 39: // right
20696                 dir = e.keyCode == 37 ? -1 : 1;
20697                 
20698                 this.vIndex = this.vIndex + dir;
20699                 
20700                 if(this.vIndex < 0){
20701                     this.vIndex = 0;
20702                 }
20703                 
20704                 if(this.vIndex > 11){
20705                     this.vIndex = 11;
20706                 }
20707                 
20708                 if(isNaN(this.vIndex)){
20709                     this.vIndex = 0;
20710                 }
20711                 
20712                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20713                 
20714                 break;
20715             case 38: // up
20716             case 40: // down
20717                 
20718                 dir = e.keyCode == 38 ? -1 : 1;
20719                 
20720                 this.vIndex = this.vIndex + dir * 4;
20721                 
20722                 if(this.vIndex < 0){
20723                     this.vIndex = 0;
20724                 }
20725                 
20726                 if(this.vIndex > 11){
20727                     this.vIndex = 11;
20728                 }
20729                 
20730                 if(isNaN(this.vIndex)){
20731                     this.vIndex = 0;
20732                 }
20733                 
20734                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20735                 break;
20736                 
20737             case 13: // enter
20738                 
20739                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20740                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20741                 }
20742                 
20743                 this.hide();
20744                 e.preventDefault();
20745                 break;
20746             case 9: // tab
20747                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20748                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20749                 }
20750                 this.hide();
20751                 break;
20752             case 16: // shift
20753             case 17: // ctrl
20754             case 18: // alt
20755                 break;
20756             default :
20757                 this.hide();
20758                 
20759         }
20760     },
20761     
20762     remove: function() 
20763     {
20764         this.picker().remove();
20765     }
20766    
20767 });
20768
20769 Roo.apply(Roo.bootstrap.MonthField,  {
20770     
20771     content : {
20772         tag: 'tbody',
20773         cn: [
20774         {
20775             tag: 'tr',
20776             cn: [
20777             {
20778                 tag: 'td',
20779                 colspan: '7'
20780             }
20781             ]
20782         }
20783         ]
20784     },
20785     
20786     dates:{
20787         en: {
20788             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20789             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20790         }
20791     }
20792 });
20793
20794 Roo.apply(Roo.bootstrap.MonthField,  {
20795   
20796     template : {
20797         tag: 'div',
20798         cls: 'datepicker dropdown-menu roo-dynamic',
20799         cn: [
20800             {
20801                 tag: 'div',
20802                 cls: 'datepicker-months',
20803                 cn: [
20804                 {
20805                     tag: 'table',
20806                     cls: 'table-condensed',
20807                     cn:[
20808                         Roo.bootstrap.DateField.content
20809                     ]
20810                 }
20811                 ]
20812             }
20813         ]
20814     }
20815 });
20816
20817  
20818
20819  
20820  /*
20821  * - LGPL
20822  *
20823  * CheckBox
20824  * 
20825  */
20826
20827 /**
20828  * @class Roo.bootstrap.CheckBox
20829  * @extends Roo.bootstrap.Input
20830  * Bootstrap CheckBox class
20831  * 
20832  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20833  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20834  * @cfg {String} boxLabel The text that appears beside the checkbox
20835  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20836  * @cfg {Boolean} checked initnal the element
20837  * @cfg {Boolean} inline inline the element (default false)
20838  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20839  * @cfg {String} tooltip label tooltip
20840  * 
20841  * @constructor
20842  * Create a new CheckBox
20843  * @param {Object} config The config object
20844  */
20845
20846 Roo.bootstrap.CheckBox = function(config){
20847     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20848    
20849     this.addEvents({
20850         /**
20851         * @event check
20852         * Fires when the element is checked or unchecked.
20853         * @param {Roo.bootstrap.CheckBox} this This input
20854         * @param {Boolean} checked The new checked value
20855         */
20856        check : true,
20857        /**
20858         * @event click
20859         * Fires when the element is click.
20860         * @param {Roo.bootstrap.CheckBox} this This input
20861         */
20862        click : true
20863     });
20864     
20865 };
20866
20867 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20868   
20869     inputType: 'checkbox',
20870     inputValue: 1,
20871     valueOff: 0,
20872     boxLabel: false,
20873     checked: false,
20874     weight : false,
20875     inline: false,
20876     tooltip : '',
20877     
20878     getAutoCreate : function()
20879     {
20880         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20881         
20882         var id = Roo.id();
20883         
20884         var cfg = {};
20885         
20886         cfg.cls = 'form-group ' + this.inputType; //input-group
20887         
20888         if(this.inline){
20889             cfg.cls += ' ' + this.inputType + '-inline';
20890         }
20891         
20892         var input =  {
20893             tag: 'input',
20894             id : id,
20895             type : this.inputType,
20896             value : this.inputValue,
20897             cls : 'roo-' + this.inputType, //'form-box',
20898             placeholder : this.placeholder || ''
20899             
20900         };
20901         
20902         if(this.inputType != 'radio'){
20903             var hidden =  {
20904                 tag: 'input',
20905                 type : 'hidden',
20906                 cls : 'roo-hidden-value',
20907                 value : this.checked ? this.inputValue : this.valueOff
20908             };
20909         }
20910         
20911             
20912         if (this.weight) { // Validity check?
20913             cfg.cls += " " + this.inputType + "-" + this.weight;
20914         }
20915         
20916         if (this.disabled) {
20917             input.disabled=true;
20918         }
20919         
20920         if(this.checked){
20921             input.checked = this.checked;
20922         }
20923         
20924         if (this.name) {
20925             
20926             input.name = this.name;
20927             
20928             if(this.inputType != 'radio'){
20929                 hidden.name = this.name;
20930                 input.name = '_hidden_' + this.name;
20931             }
20932         }
20933         
20934         if (this.size) {
20935             input.cls += ' input-' + this.size;
20936         }
20937         
20938         var settings=this;
20939         
20940         ['xs','sm','md','lg'].map(function(size){
20941             if (settings[size]) {
20942                 cfg.cls += ' col-' + size + '-' + settings[size];
20943             }
20944         });
20945         
20946         var inputblock = input;
20947          
20948         if (this.before || this.after) {
20949             
20950             inputblock = {
20951                 cls : 'input-group',
20952                 cn :  [] 
20953             };
20954             
20955             if (this.before) {
20956                 inputblock.cn.push({
20957                     tag :'span',
20958                     cls : 'input-group-addon',
20959                     html : this.before
20960                 });
20961             }
20962             
20963             inputblock.cn.push(input);
20964             
20965             if(this.inputType != 'radio'){
20966                 inputblock.cn.push(hidden);
20967             }
20968             
20969             if (this.after) {
20970                 inputblock.cn.push({
20971                     tag :'span',
20972                     cls : 'input-group-addon',
20973                     html : this.after
20974                 });
20975             }
20976             
20977         }
20978         
20979         if (align ==='left' && this.fieldLabel.length) {
20980 //                Roo.log("left and has label");
20981             cfg.cn = [
20982                 {
20983                     tag: 'label',
20984                     'for' :  id,
20985                     cls : 'control-label',
20986                     html : this.fieldLabel
20987                 },
20988                 {
20989                     cls : "", 
20990                     cn: [
20991                         inputblock
20992                     ]
20993                 }
20994             ];
20995             
20996             if(this.labelWidth > 12){
20997                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20998             }
20999             
21000             if(this.labelWidth < 13 && this.labelmd == 0){
21001                 this.labelmd = this.labelWidth;
21002             }
21003             
21004             if(this.labellg > 0){
21005                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
21006                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
21007             }
21008             
21009             if(this.labelmd > 0){
21010                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
21011                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
21012             }
21013             
21014             if(this.labelsm > 0){
21015                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
21016                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
21017             }
21018             
21019             if(this.labelxs > 0){
21020                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
21021                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
21022             }
21023             
21024         } else if ( this.fieldLabel.length) {
21025 //                Roo.log(" label");
21026                 cfg.cn = [
21027                    
21028                     {
21029                         tag: this.boxLabel ? 'span' : 'label',
21030                         'for': id,
21031                         cls: 'control-label box-input-label',
21032                         //cls : 'input-group-addon',
21033                         html : this.fieldLabel
21034                     },
21035                     
21036                     inputblock
21037                     
21038                 ];
21039
21040         } else {
21041             
21042 //                Roo.log(" no label && no align");
21043                 cfg.cn = [  inputblock ] ;
21044                 
21045                 
21046         }
21047         
21048         if(this.boxLabel){
21049              var boxLabelCfg = {
21050                 tag: 'label',
21051                 //'for': id, // box label is handled by onclick - so no for...
21052                 cls: 'box-label',
21053                 html: this.boxLabel
21054             };
21055             
21056             if(this.tooltip){
21057                 boxLabelCfg.tooltip = this.tooltip;
21058             }
21059              
21060             cfg.cn.push(boxLabelCfg);
21061         }
21062         
21063         if(this.inputType != 'radio'){
21064             cfg.cn.push(hidden);
21065         }
21066         
21067         return cfg;
21068         
21069     },
21070     
21071     /**
21072      * return the real input element.
21073      */
21074     inputEl: function ()
21075     {
21076         return this.el.select('input.roo-' + this.inputType,true).first();
21077     },
21078     hiddenEl: function ()
21079     {
21080         return this.el.select('input.roo-hidden-value',true).first();
21081     },
21082     
21083     labelEl: function()
21084     {
21085         return this.el.select('label.control-label',true).first();
21086     },
21087     /* depricated... */
21088     
21089     label: function()
21090     {
21091         return this.labelEl();
21092     },
21093     
21094     boxLabelEl: function()
21095     {
21096         return this.el.select('label.box-label',true).first();
21097     },
21098     
21099     initEvents : function()
21100     {
21101 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21102         
21103         this.inputEl().on('click', this.onClick,  this);
21104         
21105         if (this.boxLabel) { 
21106             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21107         }
21108         
21109         this.startValue = this.getValue();
21110         
21111         if(this.groupId){
21112             Roo.bootstrap.CheckBox.register(this);
21113         }
21114     },
21115     
21116     onClick : function(e)
21117     {   
21118         if(this.fireEvent('click', this, e) !== false){
21119             this.setChecked(!this.checked);
21120         }
21121         
21122     },
21123     
21124     setChecked : function(state,suppressEvent)
21125     {
21126         this.startValue = this.getValue();
21127
21128         if(this.inputType == 'radio'){
21129             
21130             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21131                 e.dom.checked = false;
21132             });
21133             
21134             this.inputEl().dom.checked = true;
21135             
21136             this.inputEl().dom.value = this.inputValue;
21137             
21138             if(suppressEvent !== true){
21139                 this.fireEvent('check', this, true);
21140             }
21141             
21142             this.validate();
21143             
21144             return;
21145         }
21146         
21147         this.checked = state;
21148         
21149         this.inputEl().dom.checked = state;
21150         
21151         
21152         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21153         
21154         if(suppressEvent !== true){
21155             this.fireEvent('check', this, state);
21156         }
21157         
21158         this.validate();
21159     },
21160     
21161     getValue : function()
21162     {
21163         if(this.inputType == 'radio'){
21164             return this.getGroupValue();
21165         }
21166         
21167         return this.hiddenEl().dom.value;
21168         
21169     },
21170     
21171     getGroupValue : function()
21172     {
21173         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21174             return '';
21175         }
21176         
21177         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21178     },
21179     
21180     setValue : function(v,suppressEvent)
21181     {
21182         if(this.inputType == 'radio'){
21183             this.setGroupValue(v, suppressEvent);
21184             return;
21185         }
21186         
21187         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21188         
21189         this.validate();
21190     },
21191     
21192     setGroupValue : function(v, suppressEvent)
21193     {
21194         this.startValue = this.getValue();
21195         
21196         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21197             e.dom.checked = false;
21198             
21199             if(e.dom.value == v){
21200                 e.dom.checked = true;
21201             }
21202         });
21203         
21204         if(suppressEvent !== true){
21205             this.fireEvent('check', this, true);
21206         }
21207
21208         this.validate();
21209         
21210         return;
21211     },
21212     
21213     validate : function()
21214     {
21215         if(this.getVisibilityEl().hasClass('hidden')){
21216             return true;
21217         }
21218         
21219         if(
21220                 this.disabled || 
21221                 (this.inputType == 'radio' && this.validateRadio()) ||
21222                 (this.inputType == 'checkbox' && this.validateCheckbox())
21223         ){
21224             this.markValid();
21225             return true;
21226         }
21227         
21228         this.markInvalid();
21229         return false;
21230     },
21231     
21232     validateRadio : function()
21233     {
21234         if(this.getVisibilityEl().hasClass('hidden')){
21235             return true;
21236         }
21237         
21238         if(this.allowBlank){
21239             return true;
21240         }
21241         
21242         var valid = false;
21243         
21244         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21245             if(!e.dom.checked){
21246                 return;
21247             }
21248             
21249             valid = true;
21250             
21251             return false;
21252         });
21253         
21254         return valid;
21255     },
21256     
21257     validateCheckbox : function()
21258     {
21259         if(!this.groupId){
21260             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21261             //return (this.getValue() == this.inputValue) ? true : false;
21262         }
21263         
21264         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21265         
21266         if(!group){
21267             return false;
21268         }
21269         
21270         var r = false;
21271         
21272         for(var i in group){
21273             if(group[i].el.isVisible(true)){
21274                 r = false;
21275                 break;
21276             }
21277             
21278             r = true;
21279         }
21280         
21281         for(var i in group){
21282             if(r){
21283                 break;
21284             }
21285             
21286             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21287         }
21288         
21289         return r;
21290     },
21291     
21292     /**
21293      * Mark this field as valid
21294      */
21295     markValid : function()
21296     {
21297         var _this = this;
21298         
21299         this.fireEvent('valid', this);
21300         
21301         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21302         
21303         if(this.groupId){
21304             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21305         }
21306         
21307         if(label){
21308             label.markValid();
21309         }
21310
21311         if(this.inputType == 'radio'){
21312             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21313                 var fg = e.findParent('.form-group', false, true);
21314                 if (Roo.bootstrap.version == 3) {
21315                     fg.removeClass([_this.invalidClass, _this.validClass]);
21316                     fg.addClass(_this.validClass);
21317                 } else {
21318                     fg.removeClass(['is-valid', 'is-invalid']);
21319                     fg.addClass('is-valid');
21320                 }
21321             });
21322             
21323             return;
21324         }
21325
21326         if(!this.groupId){
21327             var fg = this.el.findParent('.form-group', false, true);
21328             if (Roo.bootstrap.version == 3) {
21329                 fg.removeClass([this.invalidClass, this.validClass]);
21330                 fg.addClass(this.validClass);
21331             } else {
21332                 fg.removeClass(['is-valid', 'is-invalid']);
21333                 fg.addClass('is-valid');
21334             }
21335             return;
21336         }
21337         
21338         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21339         
21340         if(!group){
21341             return;
21342         }
21343         
21344         for(var i in group){
21345             var fg = group[i].el.findParent('.form-group', false, true);
21346             if (Roo.bootstrap.version == 3) {
21347                 fg.removeClass([this.invalidClass, this.validClass]);
21348                 fg.addClass(this.validClass);
21349             } else {
21350                 fg.removeClass(['is-valid', 'is-invalid']);
21351                 fg.addClass('is-valid');
21352             }
21353         }
21354     },
21355     
21356      /**
21357      * Mark this field as invalid
21358      * @param {String} msg The validation message
21359      */
21360     markInvalid : function(msg)
21361     {
21362         if(this.allowBlank){
21363             return;
21364         }
21365         
21366         var _this = this;
21367         
21368         this.fireEvent('invalid', this, msg);
21369         
21370         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21371         
21372         if(this.groupId){
21373             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21374         }
21375         
21376         if(label){
21377             label.markInvalid();
21378         }
21379             
21380         if(this.inputType == 'radio'){
21381             
21382             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21383                 var fg = e.findParent('.form-group', false, true);
21384                 if (Roo.bootstrap.version == 3) {
21385                     fg.removeClass([_this.invalidClass, _this.validClass]);
21386                     fg.addClass(_this.invalidClass);
21387                 } else {
21388                     fg.removeClass(['is-invalid', 'is-valid']);
21389                     fg.addClass('is-invalid');
21390                 }
21391             });
21392             
21393             return;
21394         }
21395         
21396         if(!this.groupId){
21397             var fg = this.el.findParent('.form-group', false, true);
21398             if (Roo.bootstrap.version == 3) {
21399                 fg.removeClass([_this.invalidClass, _this.validClass]);
21400                 fg.addClass(_this.invalidClass);
21401             } else {
21402                 fg.removeClass(['is-invalid', 'is-valid']);
21403                 fg.addClass('is-invalid');
21404             }
21405             return;
21406         }
21407         
21408         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21409         
21410         if(!group){
21411             return;
21412         }
21413         
21414         for(var i in group){
21415             var fg = group[i].el.findParent('.form-group', false, true);
21416             if (Roo.bootstrap.version == 3) {
21417                 fg.removeClass([_this.invalidClass, _this.validClass]);
21418                 fg.addClass(_this.invalidClass);
21419             } else {
21420                 fg.removeClass(['is-invalid', 'is-valid']);
21421                 fg.addClass('is-invalid');
21422             }
21423         }
21424         
21425     },
21426     
21427     clearInvalid : function()
21428     {
21429         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21430         
21431         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21432         
21433         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21434         
21435         if (label && label.iconEl) {
21436             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21437             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21438         }
21439     },
21440     
21441     disable : function()
21442     {
21443         if(this.inputType != 'radio'){
21444             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21445             return;
21446         }
21447         
21448         var _this = this;
21449         
21450         if(this.rendered){
21451             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21452                 _this.getActionEl().addClass(this.disabledClass);
21453                 e.dom.disabled = true;
21454             });
21455         }
21456         
21457         this.disabled = true;
21458         this.fireEvent("disable", this);
21459         return this;
21460     },
21461
21462     enable : function()
21463     {
21464         if(this.inputType != 'radio'){
21465             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21466             return;
21467         }
21468         
21469         var _this = this;
21470         
21471         if(this.rendered){
21472             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21473                 _this.getActionEl().removeClass(this.disabledClass);
21474                 e.dom.disabled = false;
21475             });
21476         }
21477         
21478         this.disabled = false;
21479         this.fireEvent("enable", this);
21480         return this;
21481     },
21482     
21483     setBoxLabel : function(v)
21484     {
21485         this.boxLabel = v;
21486         
21487         if(this.rendered){
21488             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21489         }
21490     }
21491
21492 });
21493
21494 Roo.apply(Roo.bootstrap.CheckBox, {
21495     
21496     groups: {},
21497     
21498      /**
21499     * register a CheckBox Group
21500     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21501     */
21502     register : function(checkbox)
21503     {
21504         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21505             this.groups[checkbox.groupId] = {};
21506         }
21507         
21508         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21509             return;
21510         }
21511         
21512         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21513         
21514     },
21515     /**
21516     * fetch a CheckBox Group based on the group ID
21517     * @param {string} the group ID
21518     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21519     */
21520     get: function(groupId) {
21521         if (typeof(this.groups[groupId]) == 'undefined') {
21522             return false;
21523         }
21524         
21525         return this.groups[groupId] ;
21526     }
21527     
21528     
21529 });
21530 /*
21531  * - LGPL
21532  *
21533  * RadioItem
21534  * 
21535  */
21536
21537 /**
21538  * @class Roo.bootstrap.Radio
21539  * @extends Roo.bootstrap.Component
21540  * Bootstrap Radio class
21541  * @cfg {String} boxLabel - the label associated
21542  * @cfg {String} value - the value of radio
21543  * 
21544  * @constructor
21545  * Create a new Radio
21546  * @param {Object} config The config object
21547  */
21548 Roo.bootstrap.Radio = function(config){
21549     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21550     
21551 };
21552
21553 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21554     
21555     boxLabel : '',
21556     
21557     value : '',
21558     
21559     getAutoCreate : function()
21560     {
21561         var cfg = {
21562             tag : 'div',
21563             cls : 'form-group radio',
21564             cn : [
21565                 {
21566                     tag : 'label',
21567                     cls : 'box-label',
21568                     html : this.boxLabel
21569                 }
21570             ]
21571         };
21572         
21573         return cfg;
21574     },
21575     
21576     initEvents : function() 
21577     {
21578         this.parent().register(this);
21579         
21580         this.el.on('click', this.onClick, this);
21581         
21582     },
21583     
21584     onClick : function(e)
21585     {
21586         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21587             this.setChecked(true);
21588         }
21589     },
21590     
21591     setChecked : function(state, suppressEvent)
21592     {
21593         this.parent().setValue(this.value, suppressEvent);
21594         
21595     },
21596     
21597     setBoxLabel : function(v)
21598     {
21599         this.boxLabel = v;
21600         
21601         if(this.rendered){
21602             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21603         }
21604     }
21605     
21606 });
21607  
21608
21609  /*
21610  * - LGPL
21611  *
21612  * Input
21613  * 
21614  */
21615
21616 /**
21617  * @class Roo.bootstrap.SecurePass
21618  * @extends Roo.bootstrap.Input
21619  * Bootstrap SecurePass class
21620  *
21621  * 
21622  * @constructor
21623  * Create a new SecurePass
21624  * @param {Object} config The config object
21625  */
21626  
21627 Roo.bootstrap.SecurePass = function (config) {
21628     // these go here, so the translation tool can replace them..
21629     this.errors = {
21630         PwdEmpty: "Please type a password, and then retype it to confirm.",
21631         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21632         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21633         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21634         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21635         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21636         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21637         TooWeak: "Your password is Too Weak."
21638     },
21639     this.meterLabel = "Password strength:";
21640     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21641     this.meterClass = [
21642         "roo-password-meter-tooweak", 
21643         "roo-password-meter-weak", 
21644         "roo-password-meter-medium", 
21645         "roo-password-meter-strong", 
21646         "roo-password-meter-grey"
21647     ];
21648     
21649     this.errors = {};
21650     
21651     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21652 }
21653
21654 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21655     /**
21656      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21657      * {
21658      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21659      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21660      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21661      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21662      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21663      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21664      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21665      * })
21666      */
21667     // private
21668     
21669     meterWidth: 300,
21670     errorMsg :'',    
21671     errors: false,
21672     imageRoot: '/',
21673     /**
21674      * @cfg {String/Object} Label for the strength meter (defaults to
21675      * 'Password strength:')
21676      */
21677     // private
21678     meterLabel: '',
21679     /**
21680      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21681      * ['Weak', 'Medium', 'Strong'])
21682      */
21683     // private    
21684     pwdStrengths: false,    
21685     // private
21686     strength: 0,
21687     // private
21688     _lastPwd: null,
21689     // private
21690     kCapitalLetter: 0,
21691     kSmallLetter: 1,
21692     kDigit: 2,
21693     kPunctuation: 3,
21694     
21695     insecure: false,
21696     // private
21697     initEvents: function ()
21698     {
21699         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21700
21701         if (this.el.is('input[type=password]') && Roo.isSafari) {
21702             this.el.on('keydown', this.SafariOnKeyDown, this);
21703         }
21704
21705         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21706     },
21707     // private
21708     onRender: function (ct, position)
21709     {
21710         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21711         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21712         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21713
21714         this.trigger.createChild({
21715                    cn: [
21716                     {
21717                     //id: 'PwdMeter',
21718                     tag: 'div',
21719                     cls: 'roo-password-meter-grey col-xs-12',
21720                     style: {
21721                         //width: 0,
21722                         //width: this.meterWidth + 'px'                                                
21723                         }
21724                     },
21725                     {                            
21726                          cls: 'roo-password-meter-text'                          
21727                     }
21728                 ]            
21729         });
21730
21731          
21732         if (this.hideTrigger) {
21733             this.trigger.setDisplayed(false);
21734         }
21735         this.setSize(this.width || '', this.height || '');
21736     },
21737     // private
21738     onDestroy: function ()
21739     {
21740         if (this.trigger) {
21741             this.trigger.removeAllListeners();
21742             this.trigger.remove();
21743         }
21744         if (this.wrap) {
21745             this.wrap.remove();
21746         }
21747         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21748     },
21749     // private
21750     checkStrength: function ()
21751     {
21752         var pwd = this.inputEl().getValue();
21753         if (pwd == this._lastPwd) {
21754             return;
21755         }
21756
21757         var strength;
21758         if (this.ClientSideStrongPassword(pwd)) {
21759             strength = 3;
21760         } else if (this.ClientSideMediumPassword(pwd)) {
21761             strength = 2;
21762         } else if (this.ClientSideWeakPassword(pwd)) {
21763             strength = 1;
21764         } else {
21765             strength = 0;
21766         }
21767         
21768         Roo.log('strength1: ' + strength);
21769         
21770         //var pm = this.trigger.child('div/div/div').dom;
21771         var pm = this.trigger.child('div/div');
21772         pm.removeClass(this.meterClass);
21773         pm.addClass(this.meterClass[strength]);
21774                 
21775         
21776         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21777                 
21778         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21779         
21780         this._lastPwd = pwd;
21781     },
21782     reset: function ()
21783     {
21784         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21785         
21786         this._lastPwd = '';
21787         
21788         var pm = this.trigger.child('div/div');
21789         pm.removeClass(this.meterClass);
21790         pm.addClass('roo-password-meter-grey');        
21791         
21792         
21793         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21794         
21795         pt.innerHTML = '';
21796         this.inputEl().dom.type='password';
21797     },
21798     // private
21799     validateValue: function (value)
21800     {
21801         
21802         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21803             return false;
21804         }
21805         if (value.length == 0) {
21806             if (this.allowBlank) {
21807                 this.clearInvalid();
21808                 return true;
21809             }
21810
21811             this.markInvalid(this.errors.PwdEmpty);
21812             this.errorMsg = this.errors.PwdEmpty;
21813             return false;
21814         }
21815         
21816         if(this.insecure){
21817             return true;
21818         }
21819         
21820         if ('[\x21-\x7e]*'.match(value)) {
21821             this.markInvalid(this.errors.PwdBadChar);
21822             this.errorMsg = this.errors.PwdBadChar;
21823             return false;
21824         }
21825         if (value.length < 6) {
21826             this.markInvalid(this.errors.PwdShort);
21827             this.errorMsg = this.errors.PwdShort;
21828             return false;
21829         }
21830         if (value.length > 16) {
21831             this.markInvalid(this.errors.PwdLong);
21832             this.errorMsg = this.errors.PwdLong;
21833             return false;
21834         }
21835         var strength;
21836         if (this.ClientSideStrongPassword(value)) {
21837             strength = 3;
21838         } else if (this.ClientSideMediumPassword(value)) {
21839             strength = 2;
21840         } else if (this.ClientSideWeakPassword(value)) {
21841             strength = 1;
21842         } else {
21843             strength = 0;
21844         }
21845
21846         
21847         if (strength < 2) {
21848             //this.markInvalid(this.errors.TooWeak);
21849             this.errorMsg = this.errors.TooWeak;
21850             //return false;
21851         }
21852         
21853         
21854         console.log('strength2: ' + strength);
21855         
21856         //var pm = this.trigger.child('div/div/div').dom;
21857         
21858         var pm = this.trigger.child('div/div');
21859         pm.removeClass(this.meterClass);
21860         pm.addClass(this.meterClass[strength]);
21861                 
21862         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21863                 
21864         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21865         
21866         this.errorMsg = ''; 
21867         return true;
21868     },
21869     // private
21870     CharacterSetChecks: function (type)
21871     {
21872         this.type = type;
21873         this.fResult = false;
21874     },
21875     // private
21876     isctype: function (character, type)
21877     {
21878         switch (type) {  
21879             case this.kCapitalLetter:
21880                 if (character >= 'A' && character <= 'Z') {
21881                     return true;
21882                 }
21883                 break;
21884             
21885             case this.kSmallLetter:
21886                 if (character >= 'a' && character <= 'z') {
21887                     return true;
21888                 }
21889                 break;
21890             
21891             case this.kDigit:
21892                 if (character >= '0' && character <= '9') {
21893                     return true;
21894                 }
21895                 break;
21896             
21897             case this.kPunctuation:
21898                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21899                     return true;
21900                 }
21901                 break;
21902             
21903             default:
21904                 return false;
21905         }
21906
21907     },
21908     // private
21909     IsLongEnough: function (pwd, size)
21910     {
21911         return !(pwd == null || isNaN(size) || pwd.length < size);
21912     },
21913     // private
21914     SpansEnoughCharacterSets: function (word, nb)
21915     {
21916         if (!this.IsLongEnough(word, nb))
21917         {
21918             return false;
21919         }
21920
21921         var characterSetChecks = new Array(
21922             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21923             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21924         );
21925         
21926         for (var index = 0; index < word.length; ++index) {
21927             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21928                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21929                     characterSetChecks[nCharSet].fResult = true;
21930                     break;
21931                 }
21932             }
21933         }
21934
21935         var nCharSets = 0;
21936         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21937             if (characterSetChecks[nCharSet].fResult) {
21938                 ++nCharSets;
21939             }
21940         }
21941
21942         if (nCharSets < nb) {
21943             return false;
21944         }
21945         return true;
21946     },
21947     // private
21948     ClientSideStrongPassword: function (pwd)
21949     {
21950         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21951     },
21952     // private
21953     ClientSideMediumPassword: function (pwd)
21954     {
21955         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21956     },
21957     // private
21958     ClientSideWeakPassword: function (pwd)
21959     {
21960         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21961     }
21962           
21963 })//<script type="text/javascript">
21964
21965 /*
21966  * Based  Ext JS Library 1.1.1
21967  * Copyright(c) 2006-2007, Ext JS, LLC.
21968  * LGPL
21969  *
21970  */
21971  
21972 /**
21973  * @class Roo.HtmlEditorCore
21974  * @extends Roo.Component
21975  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21976  *
21977  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21978  */
21979
21980 Roo.HtmlEditorCore = function(config){
21981     
21982     
21983     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21984     
21985     
21986     this.addEvents({
21987         /**
21988          * @event initialize
21989          * Fires when the editor is fully initialized (including the iframe)
21990          * @param {Roo.HtmlEditorCore} this
21991          */
21992         initialize: true,
21993         /**
21994          * @event activate
21995          * Fires when the editor is first receives the focus. Any insertion must wait
21996          * until after this event.
21997          * @param {Roo.HtmlEditorCore} this
21998          */
21999         activate: true,
22000          /**
22001          * @event beforesync
22002          * Fires before the textarea is updated with content from the editor iframe. Return false
22003          * to cancel the sync.
22004          * @param {Roo.HtmlEditorCore} this
22005          * @param {String} html
22006          */
22007         beforesync: true,
22008          /**
22009          * @event beforepush
22010          * Fires before the iframe editor is updated with content from the textarea. Return false
22011          * to cancel the push.
22012          * @param {Roo.HtmlEditorCore} this
22013          * @param {String} html
22014          */
22015         beforepush: true,
22016          /**
22017          * @event sync
22018          * Fires when the textarea is updated with content from the editor iframe.
22019          * @param {Roo.HtmlEditorCore} this
22020          * @param {String} html
22021          */
22022         sync: true,
22023          /**
22024          * @event push
22025          * Fires when the iframe editor is updated with content from the textarea.
22026          * @param {Roo.HtmlEditorCore} this
22027          * @param {String} html
22028          */
22029         push: true,
22030         
22031         /**
22032          * @event editorevent
22033          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22034          * @param {Roo.HtmlEditorCore} this
22035          */
22036         editorevent: true
22037         
22038     });
22039     
22040     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22041     
22042     // defaults : white / black...
22043     this.applyBlacklists();
22044     
22045     
22046     
22047 };
22048
22049
22050 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22051
22052
22053      /**
22054      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22055      */
22056     
22057     owner : false,
22058     
22059      /**
22060      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22061      *                        Roo.resizable.
22062      */
22063     resizable : false,
22064      /**
22065      * @cfg {Number} height (in pixels)
22066      */   
22067     height: 300,
22068    /**
22069      * @cfg {Number} width (in pixels)
22070      */   
22071     width: 500,
22072     
22073     /**
22074      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22075      * 
22076      */
22077     stylesheets: false,
22078     
22079     // id of frame..
22080     frameId: false,
22081     
22082     // private properties
22083     validationEvent : false,
22084     deferHeight: true,
22085     initialized : false,
22086     activated : false,
22087     sourceEditMode : false,
22088     onFocus : Roo.emptyFn,
22089     iframePad:3,
22090     hideMode:'offsets',
22091     
22092     clearUp: true,
22093     
22094     // blacklist + whitelisted elements..
22095     black: false,
22096     white: false,
22097      
22098     bodyCls : '',
22099
22100     /**
22101      * Protected method that will not generally be called directly. It
22102      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22103      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22104      */
22105     getDocMarkup : function(){
22106         // body styles..
22107         var st = '';
22108         
22109         // inherit styels from page...?? 
22110         if (this.stylesheets === false) {
22111             
22112             Roo.get(document.head).select('style').each(function(node) {
22113                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22114             });
22115             
22116             Roo.get(document.head).select('link').each(function(node) { 
22117                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22118             });
22119             
22120         } else if (!this.stylesheets.length) {
22121                 // simple..
22122                 st = '<style type="text/css">' +
22123                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22124                    '</style>';
22125         } else { 
22126             st = '<style type="text/css">' +
22127                     this.stylesheets +
22128                 '</style>';
22129         }
22130         
22131         st +=  '<style type="text/css">' +
22132             'IMG { cursor: pointer } ' +
22133         '</style>';
22134
22135         var cls = 'roo-htmleditor-body';
22136         
22137         if(this.bodyCls.length){
22138             cls += ' ' + this.bodyCls;
22139         }
22140         
22141         return '<html><head>' + st  +
22142             //<style type="text/css">' +
22143             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22144             //'</style>' +
22145             ' </head><body class="' +  cls + '"></body></html>';
22146     },
22147
22148     // private
22149     onRender : function(ct, position)
22150     {
22151         var _t = this;
22152         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22153         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22154         
22155         
22156         this.el.dom.style.border = '0 none';
22157         this.el.dom.setAttribute('tabIndex', -1);
22158         this.el.addClass('x-hidden hide');
22159         
22160         
22161         
22162         if(Roo.isIE){ // fix IE 1px bogus margin
22163             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22164         }
22165        
22166         
22167         this.frameId = Roo.id();
22168         
22169          
22170         
22171         var iframe = this.owner.wrap.createChild({
22172             tag: 'iframe',
22173             cls: 'form-control', // bootstrap..
22174             id: this.frameId,
22175             name: this.frameId,
22176             frameBorder : 'no',
22177             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22178         }, this.el
22179         );
22180         
22181         
22182         this.iframe = iframe.dom;
22183
22184          this.assignDocWin();
22185         
22186         this.doc.designMode = 'on';
22187        
22188         this.doc.open();
22189         this.doc.write(this.getDocMarkup());
22190         this.doc.close();
22191
22192         
22193         var task = { // must defer to wait for browser to be ready
22194             run : function(){
22195                 //console.log("run task?" + this.doc.readyState);
22196                 this.assignDocWin();
22197                 if(this.doc.body || this.doc.readyState == 'complete'){
22198                     try {
22199                         this.doc.designMode="on";
22200                     } catch (e) {
22201                         return;
22202                     }
22203                     Roo.TaskMgr.stop(task);
22204                     this.initEditor.defer(10, this);
22205                 }
22206             },
22207             interval : 10,
22208             duration: 10000,
22209             scope: this
22210         };
22211         Roo.TaskMgr.start(task);
22212
22213     },
22214
22215     // private
22216     onResize : function(w, h)
22217     {
22218          Roo.log('resize: ' +w + ',' + h );
22219         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22220         if(!this.iframe){
22221             return;
22222         }
22223         if(typeof w == 'number'){
22224             
22225             this.iframe.style.width = w + 'px';
22226         }
22227         if(typeof h == 'number'){
22228             
22229             this.iframe.style.height = h + 'px';
22230             if(this.doc){
22231                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22232             }
22233         }
22234         
22235     },
22236
22237     /**
22238      * Toggles the editor between standard and source edit mode.
22239      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22240      */
22241     toggleSourceEdit : function(sourceEditMode){
22242         
22243         this.sourceEditMode = sourceEditMode === true;
22244         
22245         if(this.sourceEditMode){
22246  
22247             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22248             
22249         }else{
22250             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22251             //this.iframe.className = '';
22252             this.deferFocus();
22253         }
22254         //this.setSize(this.owner.wrap.getSize());
22255         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22256     },
22257
22258     
22259   
22260
22261     /**
22262      * Protected method that will not generally be called directly. If you need/want
22263      * custom HTML cleanup, this is the method you should override.
22264      * @param {String} html The HTML to be cleaned
22265      * return {String} The cleaned HTML
22266      */
22267     cleanHtml : function(html){
22268         html = String(html);
22269         if(html.length > 5){
22270             if(Roo.isSafari){ // strip safari nonsense
22271                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22272             }
22273         }
22274         if(html == '&nbsp;'){
22275             html = '';
22276         }
22277         return html;
22278     },
22279
22280     /**
22281      * HTML Editor -> Textarea
22282      * Protected method that will not generally be called directly. Syncs the contents
22283      * of the editor iframe with the textarea.
22284      */
22285     syncValue : function(){
22286         if(this.initialized){
22287             var bd = (this.doc.body || this.doc.documentElement);
22288             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22289             var html = bd.innerHTML;
22290             if(Roo.isSafari){
22291                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22292                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22293                 if(m && m[1]){
22294                     html = '<div style="'+m[0]+'">' + html + '</div>';
22295                 }
22296             }
22297             html = this.cleanHtml(html);
22298             // fix up the special chars.. normaly like back quotes in word...
22299             // however we do not want to do this with chinese..
22300             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22301                 var cc = b.charCodeAt();
22302                 if (
22303                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22304                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22305                     (cc >= 0xf900 && cc < 0xfb00 )
22306                 ) {
22307                         return b;
22308                 }
22309                 return "&#"+cc+";" 
22310             });
22311             if(this.owner.fireEvent('beforesync', this, html) !== false){
22312                 this.el.dom.value = html;
22313                 this.owner.fireEvent('sync', this, html);
22314             }
22315         }
22316     },
22317
22318     /**
22319      * Protected method that will not generally be called directly. Pushes the value of the textarea
22320      * into the iframe editor.
22321      */
22322     pushValue : function(){
22323         if(this.initialized){
22324             var v = this.el.dom.value.trim();
22325             
22326 //            if(v.length < 1){
22327 //                v = '&#160;';
22328 //            }
22329             
22330             if(this.owner.fireEvent('beforepush', this, v) !== false){
22331                 var d = (this.doc.body || this.doc.documentElement);
22332                 d.innerHTML = v;
22333                 this.cleanUpPaste();
22334                 this.el.dom.value = d.innerHTML;
22335                 this.owner.fireEvent('push', this, v);
22336             }
22337         }
22338     },
22339
22340     // private
22341     deferFocus : function(){
22342         this.focus.defer(10, this);
22343     },
22344
22345     // doc'ed in Field
22346     focus : function(){
22347         if(this.win && !this.sourceEditMode){
22348             this.win.focus();
22349         }else{
22350             this.el.focus();
22351         }
22352     },
22353     
22354     assignDocWin: function()
22355     {
22356         var iframe = this.iframe;
22357         
22358          if(Roo.isIE){
22359             this.doc = iframe.contentWindow.document;
22360             this.win = iframe.contentWindow;
22361         } else {
22362 //            if (!Roo.get(this.frameId)) {
22363 //                return;
22364 //            }
22365 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22366 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22367             
22368             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22369                 return;
22370             }
22371             
22372             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22373             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22374         }
22375     },
22376     
22377     // private
22378     initEditor : function(){
22379         //console.log("INIT EDITOR");
22380         this.assignDocWin();
22381         
22382         
22383         
22384         this.doc.designMode="on";
22385         this.doc.open();
22386         this.doc.write(this.getDocMarkup());
22387         this.doc.close();
22388         
22389         var dbody = (this.doc.body || this.doc.documentElement);
22390         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22391         // this copies styles from the containing element into thsi one..
22392         // not sure why we need all of this..
22393         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22394         
22395         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22396         //ss['background-attachment'] = 'fixed'; // w3c
22397         dbody.bgProperties = 'fixed'; // ie
22398         //Roo.DomHelper.applyStyles(dbody, ss);
22399         Roo.EventManager.on(this.doc, {
22400             //'mousedown': this.onEditorEvent,
22401             'mouseup': this.onEditorEvent,
22402             'dblclick': this.onEditorEvent,
22403             'click': this.onEditorEvent,
22404             'keyup': this.onEditorEvent,
22405             buffer:100,
22406             scope: this
22407         });
22408         if(Roo.isGecko){
22409             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22410         }
22411         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22412             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22413         }
22414         this.initialized = true;
22415
22416         this.owner.fireEvent('initialize', this);
22417         this.pushValue();
22418     },
22419
22420     // private
22421     onDestroy : function(){
22422         
22423         
22424         
22425         if(this.rendered){
22426             
22427             //for (var i =0; i < this.toolbars.length;i++) {
22428             //    // fixme - ask toolbars for heights?
22429             //    this.toolbars[i].onDestroy();
22430            // }
22431             
22432             //this.wrap.dom.innerHTML = '';
22433             //this.wrap.remove();
22434         }
22435     },
22436
22437     // private
22438     onFirstFocus : function(){
22439         
22440         this.assignDocWin();
22441         
22442         
22443         this.activated = true;
22444          
22445     
22446         if(Roo.isGecko){ // prevent silly gecko errors
22447             this.win.focus();
22448             var s = this.win.getSelection();
22449             if(!s.focusNode || s.focusNode.nodeType != 3){
22450                 var r = s.getRangeAt(0);
22451                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22452                 r.collapse(true);
22453                 this.deferFocus();
22454             }
22455             try{
22456                 this.execCmd('useCSS', true);
22457                 this.execCmd('styleWithCSS', false);
22458             }catch(e){}
22459         }
22460         this.owner.fireEvent('activate', this);
22461     },
22462
22463     // private
22464     adjustFont: function(btn){
22465         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22466         //if(Roo.isSafari){ // safari
22467         //    adjust *= 2;
22468        // }
22469         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22470         if(Roo.isSafari){ // safari
22471             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22472             v =  (v < 10) ? 10 : v;
22473             v =  (v > 48) ? 48 : v;
22474             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22475             
22476         }
22477         
22478         
22479         v = Math.max(1, v+adjust);
22480         
22481         this.execCmd('FontSize', v  );
22482     },
22483
22484     onEditorEvent : function(e)
22485     {
22486         this.owner.fireEvent('editorevent', this, e);
22487       //  this.updateToolbar();
22488         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22489     },
22490
22491     insertTag : function(tg)
22492     {
22493         // could be a bit smarter... -> wrap the current selected tRoo..
22494         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22495             
22496             range = this.createRange(this.getSelection());
22497             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22498             wrappingNode.appendChild(range.extractContents());
22499             range.insertNode(wrappingNode);
22500
22501             return;
22502             
22503             
22504             
22505         }
22506         this.execCmd("formatblock",   tg);
22507         
22508     },
22509     
22510     insertText : function(txt)
22511     {
22512         
22513         
22514         var range = this.createRange();
22515         range.deleteContents();
22516                //alert(Sender.getAttribute('label'));
22517                
22518         range.insertNode(this.doc.createTextNode(txt));
22519     } ,
22520     
22521      
22522
22523     /**
22524      * Executes a Midas editor command on the editor document and performs necessary focus and
22525      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22526      * @param {String} cmd The Midas command
22527      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22528      */
22529     relayCmd : function(cmd, value){
22530         this.win.focus();
22531         this.execCmd(cmd, value);
22532         this.owner.fireEvent('editorevent', this);
22533         //this.updateToolbar();
22534         this.owner.deferFocus();
22535     },
22536
22537     /**
22538      * Executes a Midas editor command directly on the editor document.
22539      * For visual commands, you should use {@link #relayCmd} instead.
22540      * <b>This should only be called after the editor is initialized.</b>
22541      * @param {String} cmd The Midas command
22542      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22543      */
22544     execCmd : function(cmd, value){
22545         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22546         this.syncValue();
22547     },
22548  
22549  
22550    
22551     /**
22552      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22553      * to insert tRoo.
22554      * @param {String} text | dom node.. 
22555      */
22556     insertAtCursor : function(text)
22557     {
22558         
22559         if(!this.activated){
22560             return;
22561         }
22562         /*
22563         if(Roo.isIE){
22564             this.win.focus();
22565             var r = this.doc.selection.createRange();
22566             if(r){
22567                 r.collapse(true);
22568                 r.pasteHTML(text);
22569                 this.syncValue();
22570                 this.deferFocus();
22571             
22572             }
22573             return;
22574         }
22575         */
22576         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22577             this.win.focus();
22578             
22579             
22580             // from jquery ui (MIT licenced)
22581             var range, node;
22582             var win = this.win;
22583             
22584             if (win.getSelection && win.getSelection().getRangeAt) {
22585                 range = win.getSelection().getRangeAt(0);
22586                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22587                 range.insertNode(node);
22588             } else if (win.document.selection && win.document.selection.createRange) {
22589                 // no firefox support
22590                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22591                 win.document.selection.createRange().pasteHTML(txt);
22592             } else {
22593                 // no firefox support
22594                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22595                 this.execCmd('InsertHTML', txt);
22596             } 
22597             
22598             this.syncValue();
22599             
22600             this.deferFocus();
22601         }
22602     },
22603  // private
22604     mozKeyPress : function(e){
22605         if(e.ctrlKey){
22606             var c = e.getCharCode(), cmd;
22607           
22608             if(c > 0){
22609                 c = String.fromCharCode(c).toLowerCase();
22610                 switch(c){
22611                     case 'b':
22612                         cmd = 'bold';
22613                         break;
22614                     case 'i':
22615                         cmd = 'italic';
22616                         break;
22617                     
22618                     case 'u':
22619                         cmd = 'underline';
22620                         break;
22621                     
22622                     case 'v':
22623                         this.cleanUpPaste.defer(100, this);
22624                         return;
22625                         
22626                 }
22627                 if(cmd){
22628                     this.win.focus();
22629                     this.execCmd(cmd);
22630                     this.deferFocus();
22631                     e.preventDefault();
22632                 }
22633                 
22634             }
22635         }
22636     },
22637
22638     // private
22639     fixKeys : function(){ // load time branching for fastest keydown performance
22640         if(Roo.isIE){
22641             return function(e){
22642                 var k = e.getKey(), r;
22643                 if(k == e.TAB){
22644                     e.stopEvent();
22645                     r = this.doc.selection.createRange();
22646                     if(r){
22647                         r.collapse(true);
22648                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22649                         this.deferFocus();
22650                     }
22651                     return;
22652                 }
22653                 
22654                 if(k == e.ENTER){
22655                     r = this.doc.selection.createRange();
22656                     if(r){
22657                         var target = r.parentElement();
22658                         if(!target || target.tagName.toLowerCase() != 'li'){
22659                             e.stopEvent();
22660                             r.pasteHTML('<br />');
22661                             r.collapse(false);
22662                             r.select();
22663                         }
22664                     }
22665                 }
22666                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22667                     this.cleanUpPaste.defer(100, this);
22668                     return;
22669                 }
22670                 
22671                 
22672             };
22673         }else if(Roo.isOpera){
22674             return function(e){
22675                 var k = e.getKey();
22676                 if(k == e.TAB){
22677                     e.stopEvent();
22678                     this.win.focus();
22679                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22680                     this.deferFocus();
22681                 }
22682                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22683                     this.cleanUpPaste.defer(100, this);
22684                     return;
22685                 }
22686                 
22687             };
22688         }else if(Roo.isSafari){
22689             return function(e){
22690                 var k = e.getKey();
22691                 
22692                 if(k == e.TAB){
22693                     e.stopEvent();
22694                     this.execCmd('InsertText','\t');
22695                     this.deferFocus();
22696                     return;
22697                 }
22698                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22699                     this.cleanUpPaste.defer(100, this);
22700                     return;
22701                 }
22702                 
22703              };
22704         }
22705     }(),
22706     
22707     getAllAncestors: function()
22708     {
22709         var p = this.getSelectedNode();
22710         var a = [];
22711         if (!p) {
22712             a.push(p); // push blank onto stack..
22713             p = this.getParentElement();
22714         }
22715         
22716         
22717         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22718             a.push(p);
22719             p = p.parentNode;
22720         }
22721         a.push(this.doc.body);
22722         return a;
22723     },
22724     lastSel : false,
22725     lastSelNode : false,
22726     
22727     
22728     getSelection : function() 
22729     {
22730         this.assignDocWin();
22731         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22732     },
22733     
22734     getSelectedNode: function() 
22735     {
22736         // this may only work on Gecko!!!
22737         
22738         // should we cache this!!!!
22739         
22740         
22741         
22742          
22743         var range = this.createRange(this.getSelection()).cloneRange();
22744         
22745         if (Roo.isIE) {
22746             var parent = range.parentElement();
22747             while (true) {
22748                 var testRange = range.duplicate();
22749                 testRange.moveToElementText(parent);
22750                 if (testRange.inRange(range)) {
22751                     break;
22752                 }
22753                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22754                     break;
22755                 }
22756                 parent = parent.parentElement;
22757             }
22758             return parent;
22759         }
22760         
22761         // is ancestor a text element.
22762         var ac =  range.commonAncestorContainer;
22763         if (ac.nodeType == 3) {
22764             ac = ac.parentNode;
22765         }
22766         
22767         var ar = ac.childNodes;
22768          
22769         var nodes = [];
22770         var other_nodes = [];
22771         var has_other_nodes = false;
22772         for (var i=0;i<ar.length;i++) {
22773             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22774                 continue;
22775             }
22776             // fullly contained node.
22777             
22778             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22779                 nodes.push(ar[i]);
22780                 continue;
22781             }
22782             
22783             // probably selected..
22784             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22785                 other_nodes.push(ar[i]);
22786                 continue;
22787             }
22788             // outer..
22789             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22790                 continue;
22791             }
22792             
22793             
22794             has_other_nodes = true;
22795         }
22796         if (!nodes.length && other_nodes.length) {
22797             nodes= other_nodes;
22798         }
22799         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22800             return false;
22801         }
22802         
22803         return nodes[0];
22804     },
22805     createRange: function(sel)
22806     {
22807         // this has strange effects when using with 
22808         // top toolbar - not sure if it's a great idea.
22809         //this.editor.contentWindow.focus();
22810         if (typeof sel != "undefined") {
22811             try {
22812                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22813             } catch(e) {
22814                 return this.doc.createRange();
22815             }
22816         } else {
22817             return this.doc.createRange();
22818         }
22819     },
22820     getParentElement: function()
22821     {
22822         
22823         this.assignDocWin();
22824         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22825         
22826         var range = this.createRange(sel);
22827          
22828         try {
22829             var p = range.commonAncestorContainer;
22830             while (p.nodeType == 3) { // text node
22831                 p = p.parentNode;
22832             }
22833             return p;
22834         } catch (e) {
22835             return null;
22836         }
22837     
22838     },
22839     /***
22840      *
22841      * Range intersection.. the hard stuff...
22842      *  '-1' = before
22843      *  '0' = hits..
22844      *  '1' = after.
22845      *         [ -- selected range --- ]
22846      *   [fail]                        [fail]
22847      *
22848      *    basically..
22849      *      if end is before start or  hits it. fail.
22850      *      if start is after end or hits it fail.
22851      *
22852      *   if either hits (but other is outside. - then it's not 
22853      *   
22854      *    
22855      **/
22856     
22857     
22858     // @see http://www.thismuchiknow.co.uk/?p=64.
22859     rangeIntersectsNode : function(range, node)
22860     {
22861         var nodeRange = node.ownerDocument.createRange();
22862         try {
22863             nodeRange.selectNode(node);
22864         } catch (e) {
22865             nodeRange.selectNodeContents(node);
22866         }
22867     
22868         var rangeStartRange = range.cloneRange();
22869         rangeStartRange.collapse(true);
22870     
22871         var rangeEndRange = range.cloneRange();
22872         rangeEndRange.collapse(false);
22873     
22874         var nodeStartRange = nodeRange.cloneRange();
22875         nodeStartRange.collapse(true);
22876     
22877         var nodeEndRange = nodeRange.cloneRange();
22878         nodeEndRange.collapse(false);
22879     
22880         return rangeStartRange.compareBoundaryPoints(
22881                  Range.START_TO_START, nodeEndRange) == -1 &&
22882                rangeEndRange.compareBoundaryPoints(
22883                  Range.START_TO_START, nodeStartRange) == 1;
22884         
22885          
22886     },
22887     rangeCompareNode : function(range, node)
22888     {
22889         var nodeRange = node.ownerDocument.createRange();
22890         try {
22891             nodeRange.selectNode(node);
22892         } catch (e) {
22893             nodeRange.selectNodeContents(node);
22894         }
22895         
22896         
22897         range.collapse(true);
22898     
22899         nodeRange.collapse(true);
22900      
22901         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22902         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22903          
22904         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22905         
22906         var nodeIsBefore   =  ss == 1;
22907         var nodeIsAfter    = ee == -1;
22908         
22909         if (nodeIsBefore && nodeIsAfter) {
22910             return 0; // outer
22911         }
22912         if (!nodeIsBefore && nodeIsAfter) {
22913             return 1; //right trailed.
22914         }
22915         
22916         if (nodeIsBefore && !nodeIsAfter) {
22917             return 2;  // left trailed.
22918         }
22919         // fully contined.
22920         return 3;
22921     },
22922
22923     // private? - in a new class?
22924     cleanUpPaste :  function()
22925     {
22926         // cleans up the whole document..
22927         Roo.log('cleanuppaste');
22928         
22929         this.cleanUpChildren(this.doc.body);
22930         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22931         if (clean != this.doc.body.innerHTML) {
22932             this.doc.body.innerHTML = clean;
22933         }
22934         
22935     },
22936     
22937     cleanWordChars : function(input) {// change the chars to hex code
22938         var he = Roo.HtmlEditorCore;
22939         
22940         var output = input;
22941         Roo.each(he.swapCodes, function(sw) { 
22942             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22943             
22944             output = output.replace(swapper, sw[1]);
22945         });
22946         
22947         return output;
22948     },
22949     
22950     
22951     cleanUpChildren : function (n)
22952     {
22953         if (!n.childNodes.length) {
22954             return;
22955         }
22956         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22957            this.cleanUpChild(n.childNodes[i]);
22958         }
22959     },
22960     
22961     
22962         
22963     
22964     cleanUpChild : function (node)
22965     {
22966         var ed = this;
22967         //console.log(node);
22968         if (node.nodeName == "#text") {
22969             // clean up silly Windows -- stuff?
22970             return; 
22971         }
22972         if (node.nodeName == "#comment") {
22973             node.parentNode.removeChild(node);
22974             // clean up silly Windows -- stuff?
22975             return; 
22976         }
22977         var lcname = node.tagName.toLowerCase();
22978         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22979         // whitelist of tags..
22980         
22981         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22982             // remove node.
22983             node.parentNode.removeChild(node);
22984             return;
22985             
22986         }
22987         
22988         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22989         
22990         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22991         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22992         
22993         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22994         //    remove_keep_children = true;
22995         //}
22996         
22997         if (remove_keep_children) {
22998             this.cleanUpChildren(node);
22999             // inserts everything just before this node...
23000             while (node.childNodes.length) {
23001                 var cn = node.childNodes[0];
23002                 node.removeChild(cn);
23003                 node.parentNode.insertBefore(cn, node);
23004             }
23005             node.parentNode.removeChild(node);
23006             return;
23007         }
23008         
23009         if (!node.attributes || !node.attributes.length) {
23010             this.cleanUpChildren(node);
23011             return;
23012         }
23013         
23014         function cleanAttr(n,v)
23015         {
23016             
23017             if (v.match(/^\./) || v.match(/^\//)) {
23018                 return;
23019             }
23020             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
23021                 return;
23022             }
23023             if (v.match(/^#/)) {
23024                 return;
23025             }
23026 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
23027             node.removeAttribute(n);
23028             
23029         }
23030         
23031         var cwhite = this.cwhite;
23032         var cblack = this.cblack;
23033             
23034         function cleanStyle(n,v)
23035         {
23036             if (v.match(/expression/)) { //XSS?? should we even bother..
23037                 node.removeAttribute(n);
23038                 return;
23039             }
23040             
23041             var parts = v.split(/;/);
23042             var clean = [];
23043             
23044             Roo.each(parts, function(p) {
23045                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23046                 if (!p.length) {
23047                     return true;
23048                 }
23049                 var l = p.split(':').shift().replace(/\s+/g,'');
23050                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23051                 
23052                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23053 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23054                     //node.removeAttribute(n);
23055                     return true;
23056                 }
23057                 //Roo.log()
23058                 // only allow 'c whitelisted system attributes'
23059                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23060 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23061                     //node.removeAttribute(n);
23062                     return true;
23063                 }
23064                 
23065                 
23066                  
23067                 
23068                 clean.push(p);
23069                 return true;
23070             });
23071             if (clean.length) { 
23072                 node.setAttribute(n, clean.join(';'));
23073             } else {
23074                 node.removeAttribute(n);
23075             }
23076             
23077         }
23078         
23079         
23080         for (var i = node.attributes.length-1; i > -1 ; i--) {
23081             var a = node.attributes[i];
23082             //console.log(a);
23083             
23084             if (a.name.toLowerCase().substr(0,2)=='on')  {
23085                 node.removeAttribute(a.name);
23086                 continue;
23087             }
23088             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23089                 node.removeAttribute(a.name);
23090                 continue;
23091             }
23092             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23093                 cleanAttr(a.name,a.value); // fixme..
23094                 continue;
23095             }
23096             if (a.name == 'style') {
23097                 cleanStyle(a.name,a.value);
23098                 continue;
23099             }
23100             /// clean up MS crap..
23101             // tecnically this should be a list of valid class'es..
23102             
23103             
23104             if (a.name == 'class') {
23105                 if (a.value.match(/^Mso/)) {
23106                     node.className = '';
23107                 }
23108                 
23109                 if (a.value.match(/^body$/)) {
23110                     node.className = '';
23111                 }
23112                 continue;
23113             }
23114             
23115             // style cleanup!?
23116             // class cleanup?
23117             
23118         }
23119         
23120         
23121         this.cleanUpChildren(node);
23122         
23123         
23124     },
23125     
23126     /**
23127      * Clean up MS wordisms...
23128      */
23129     cleanWord : function(node)
23130     {
23131         
23132         
23133         if (!node) {
23134             this.cleanWord(this.doc.body);
23135             return;
23136         }
23137         if (node.nodeName == "#text") {
23138             // clean up silly Windows -- stuff?
23139             return; 
23140         }
23141         if (node.nodeName == "#comment") {
23142             node.parentNode.removeChild(node);
23143             // clean up silly Windows -- stuff?
23144             return; 
23145         }
23146         
23147         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23148             node.parentNode.removeChild(node);
23149             return;
23150         }
23151         
23152         // remove - but keep children..
23153         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23154             while (node.childNodes.length) {
23155                 var cn = node.childNodes[0];
23156                 node.removeChild(cn);
23157                 node.parentNode.insertBefore(cn, node);
23158             }
23159             node.parentNode.removeChild(node);
23160             this.iterateChildren(node, this.cleanWord);
23161             return;
23162         }
23163         // clean styles
23164         if (node.className.length) {
23165             
23166             var cn = node.className.split(/\W+/);
23167             var cna = [];
23168             Roo.each(cn, function(cls) {
23169                 if (cls.match(/Mso[a-zA-Z]+/)) {
23170                     return;
23171                 }
23172                 cna.push(cls);
23173             });
23174             node.className = cna.length ? cna.join(' ') : '';
23175             if (!cna.length) {
23176                 node.removeAttribute("class");
23177             }
23178         }
23179         
23180         if (node.hasAttribute("lang")) {
23181             node.removeAttribute("lang");
23182         }
23183         
23184         if (node.hasAttribute("style")) {
23185             
23186             var styles = node.getAttribute("style").split(";");
23187             var nstyle = [];
23188             Roo.each(styles, function(s) {
23189                 if (!s.match(/:/)) {
23190                     return;
23191                 }
23192                 var kv = s.split(":");
23193                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23194                     return;
23195                 }
23196                 // what ever is left... we allow.
23197                 nstyle.push(s);
23198             });
23199             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23200             if (!nstyle.length) {
23201                 node.removeAttribute('style');
23202             }
23203         }
23204         this.iterateChildren(node, this.cleanWord);
23205         
23206         
23207         
23208     },
23209     /**
23210      * iterateChildren of a Node, calling fn each time, using this as the scole..
23211      * @param {DomNode} node node to iterate children of.
23212      * @param {Function} fn method of this class to call on each item.
23213      */
23214     iterateChildren : function(node, fn)
23215     {
23216         if (!node.childNodes.length) {
23217                 return;
23218         }
23219         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23220            fn.call(this, node.childNodes[i])
23221         }
23222     },
23223     
23224     
23225     /**
23226      * cleanTableWidths.
23227      *
23228      * Quite often pasting from word etc.. results in tables with column and widths.
23229      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23230      *
23231      */
23232     cleanTableWidths : function(node)
23233     {
23234          
23235          
23236         if (!node) {
23237             this.cleanTableWidths(this.doc.body);
23238             return;
23239         }
23240         
23241         // ignore list...
23242         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23243             return; 
23244         }
23245         Roo.log(node.tagName);
23246         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23247             this.iterateChildren(node, this.cleanTableWidths);
23248             return;
23249         }
23250         if (node.hasAttribute('width')) {
23251             node.removeAttribute('width');
23252         }
23253         
23254          
23255         if (node.hasAttribute("style")) {
23256             // pretty basic...
23257             
23258             var styles = node.getAttribute("style").split(";");
23259             var nstyle = [];
23260             Roo.each(styles, function(s) {
23261                 if (!s.match(/:/)) {
23262                     return;
23263                 }
23264                 var kv = s.split(":");
23265                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23266                     return;
23267                 }
23268                 // what ever is left... we allow.
23269                 nstyle.push(s);
23270             });
23271             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23272             if (!nstyle.length) {
23273                 node.removeAttribute('style');
23274             }
23275         }
23276         
23277         this.iterateChildren(node, this.cleanTableWidths);
23278         
23279         
23280     },
23281     
23282     
23283     
23284     
23285     domToHTML : function(currentElement, depth, nopadtext) {
23286         
23287         depth = depth || 0;
23288         nopadtext = nopadtext || false;
23289     
23290         if (!currentElement) {
23291             return this.domToHTML(this.doc.body);
23292         }
23293         
23294         //Roo.log(currentElement);
23295         var j;
23296         var allText = false;
23297         var nodeName = currentElement.nodeName;
23298         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23299         
23300         if  (nodeName == '#text') {
23301             
23302             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23303         }
23304         
23305         
23306         var ret = '';
23307         if (nodeName != 'BODY') {
23308              
23309             var i = 0;
23310             // Prints the node tagName, such as <A>, <IMG>, etc
23311             if (tagName) {
23312                 var attr = [];
23313                 for(i = 0; i < currentElement.attributes.length;i++) {
23314                     // quoting?
23315                     var aname = currentElement.attributes.item(i).name;
23316                     if (!currentElement.attributes.item(i).value.length) {
23317                         continue;
23318                     }
23319                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23320                 }
23321                 
23322                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23323             } 
23324             else {
23325                 
23326                 // eack
23327             }
23328         } else {
23329             tagName = false;
23330         }
23331         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23332             return ret;
23333         }
23334         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23335             nopadtext = true;
23336         }
23337         
23338         
23339         // Traverse the tree
23340         i = 0;
23341         var currentElementChild = currentElement.childNodes.item(i);
23342         var allText = true;
23343         var innerHTML  = '';
23344         lastnode = '';
23345         while (currentElementChild) {
23346             // Formatting code (indent the tree so it looks nice on the screen)
23347             var nopad = nopadtext;
23348             if (lastnode == 'SPAN') {
23349                 nopad  = true;
23350             }
23351             // text
23352             if  (currentElementChild.nodeName == '#text') {
23353                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23354                 toadd = nopadtext ? toadd : toadd.trim();
23355                 if (!nopad && toadd.length > 80) {
23356                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23357                 }
23358                 innerHTML  += toadd;
23359                 
23360                 i++;
23361                 currentElementChild = currentElement.childNodes.item(i);
23362                 lastNode = '';
23363                 continue;
23364             }
23365             allText = false;
23366             
23367             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23368                 
23369             // Recursively traverse the tree structure of the child node
23370             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23371             lastnode = currentElementChild.nodeName;
23372             i++;
23373             currentElementChild=currentElement.childNodes.item(i);
23374         }
23375         
23376         ret += innerHTML;
23377         
23378         if (!allText) {
23379                 // The remaining code is mostly for formatting the tree
23380             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23381         }
23382         
23383         
23384         if (tagName) {
23385             ret+= "</"+tagName+">";
23386         }
23387         return ret;
23388         
23389     },
23390         
23391     applyBlacklists : function()
23392     {
23393         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23394         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23395         
23396         this.white = [];
23397         this.black = [];
23398         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23399             if (b.indexOf(tag) > -1) {
23400                 return;
23401             }
23402             this.white.push(tag);
23403             
23404         }, this);
23405         
23406         Roo.each(w, function(tag) {
23407             if (b.indexOf(tag) > -1) {
23408                 return;
23409             }
23410             if (this.white.indexOf(tag) > -1) {
23411                 return;
23412             }
23413             this.white.push(tag);
23414             
23415         }, this);
23416         
23417         
23418         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23419             if (w.indexOf(tag) > -1) {
23420                 return;
23421             }
23422             this.black.push(tag);
23423             
23424         }, this);
23425         
23426         Roo.each(b, function(tag) {
23427             if (w.indexOf(tag) > -1) {
23428                 return;
23429             }
23430             if (this.black.indexOf(tag) > -1) {
23431                 return;
23432             }
23433             this.black.push(tag);
23434             
23435         }, this);
23436         
23437         
23438         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23439         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23440         
23441         this.cwhite = [];
23442         this.cblack = [];
23443         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23444             if (b.indexOf(tag) > -1) {
23445                 return;
23446             }
23447             this.cwhite.push(tag);
23448             
23449         }, this);
23450         
23451         Roo.each(w, function(tag) {
23452             if (b.indexOf(tag) > -1) {
23453                 return;
23454             }
23455             if (this.cwhite.indexOf(tag) > -1) {
23456                 return;
23457             }
23458             this.cwhite.push(tag);
23459             
23460         }, this);
23461         
23462         
23463         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23464             if (w.indexOf(tag) > -1) {
23465                 return;
23466             }
23467             this.cblack.push(tag);
23468             
23469         }, this);
23470         
23471         Roo.each(b, function(tag) {
23472             if (w.indexOf(tag) > -1) {
23473                 return;
23474             }
23475             if (this.cblack.indexOf(tag) > -1) {
23476                 return;
23477             }
23478             this.cblack.push(tag);
23479             
23480         }, this);
23481     },
23482     
23483     setStylesheets : function(stylesheets)
23484     {
23485         if(typeof(stylesheets) == 'string'){
23486             Roo.get(this.iframe.contentDocument.head).createChild({
23487                 tag : 'link',
23488                 rel : 'stylesheet',
23489                 type : 'text/css',
23490                 href : stylesheets
23491             });
23492             
23493             return;
23494         }
23495         var _this = this;
23496      
23497         Roo.each(stylesheets, function(s) {
23498             if(!s.length){
23499                 return;
23500             }
23501             
23502             Roo.get(_this.iframe.contentDocument.head).createChild({
23503                 tag : 'link',
23504                 rel : 'stylesheet',
23505                 type : 'text/css',
23506                 href : s
23507             });
23508         });
23509
23510         
23511     },
23512     
23513     removeStylesheets : function()
23514     {
23515         var _this = this;
23516         
23517         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23518             s.remove();
23519         });
23520     },
23521     
23522     setStyle : function(style)
23523     {
23524         Roo.get(this.iframe.contentDocument.head).createChild({
23525             tag : 'style',
23526             type : 'text/css',
23527             html : style
23528         });
23529
23530         return;
23531     }
23532     
23533     // hide stuff that is not compatible
23534     /**
23535      * @event blur
23536      * @hide
23537      */
23538     /**
23539      * @event change
23540      * @hide
23541      */
23542     /**
23543      * @event focus
23544      * @hide
23545      */
23546     /**
23547      * @event specialkey
23548      * @hide
23549      */
23550     /**
23551      * @cfg {String} fieldClass @hide
23552      */
23553     /**
23554      * @cfg {String} focusClass @hide
23555      */
23556     /**
23557      * @cfg {String} autoCreate @hide
23558      */
23559     /**
23560      * @cfg {String} inputType @hide
23561      */
23562     /**
23563      * @cfg {String} invalidClass @hide
23564      */
23565     /**
23566      * @cfg {String} invalidText @hide
23567      */
23568     /**
23569      * @cfg {String} msgFx @hide
23570      */
23571     /**
23572      * @cfg {String} validateOnBlur @hide
23573      */
23574 });
23575
23576 Roo.HtmlEditorCore.white = [
23577         'area', 'br', 'img', 'input', 'hr', 'wbr',
23578         
23579        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23580        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23581        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23582        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23583        'table',   'ul',         'xmp', 
23584        
23585        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23586       'thead',   'tr', 
23587      
23588       'dir', 'menu', 'ol', 'ul', 'dl',
23589        
23590       'embed',  'object'
23591 ];
23592
23593
23594 Roo.HtmlEditorCore.black = [
23595     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23596         'applet', // 
23597         'base',   'basefont', 'bgsound', 'blink',  'body', 
23598         'frame',  'frameset', 'head',    'html',   'ilayer', 
23599         'iframe', 'layer',  'link',     'meta',    'object',   
23600         'script', 'style' ,'title',  'xml' // clean later..
23601 ];
23602 Roo.HtmlEditorCore.clean = [
23603     'script', 'style', 'title', 'xml'
23604 ];
23605 Roo.HtmlEditorCore.remove = [
23606     'font'
23607 ];
23608 // attributes..
23609
23610 Roo.HtmlEditorCore.ablack = [
23611     'on'
23612 ];
23613     
23614 Roo.HtmlEditorCore.aclean = [ 
23615     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23616 ];
23617
23618 // protocols..
23619 Roo.HtmlEditorCore.pwhite= [
23620         'http',  'https',  'mailto'
23621 ];
23622
23623 // white listed style attributes.
23624 Roo.HtmlEditorCore.cwhite= [
23625       //  'text-align', /// default is to allow most things..
23626       
23627          
23628 //        'font-size'//??
23629 ];
23630
23631 // black listed style attributes.
23632 Roo.HtmlEditorCore.cblack= [
23633       //  'font-size' -- this can be set by the project 
23634 ];
23635
23636
23637 Roo.HtmlEditorCore.swapCodes   =[ 
23638     [    8211, "--" ], 
23639     [    8212, "--" ], 
23640     [    8216,  "'" ],  
23641     [    8217, "'" ],  
23642     [    8220, '"' ],  
23643     [    8221, '"' ],  
23644     [    8226, "*" ],  
23645     [    8230, "..." ]
23646 ]; 
23647
23648     /*
23649  * - LGPL
23650  *
23651  * HtmlEditor
23652  * 
23653  */
23654
23655 /**
23656  * @class Roo.bootstrap.HtmlEditor
23657  * @extends Roo.bootstrap.TextArea
23658  * Bootstrap HtmlEditor class
23659
23660  * @constructor
23661  * Create a new HtmlEditor
23662  * @param {Object} config The config object
23663  */
23664
23665 Roo.bootstrap.HtmlEditor = function(config){
23666     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23667     if (!this.toolbars) {
23668         this.toolbars = [];
23669     }
23670     
23671     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23672     this.addEvents({
23673             /**
23674              * @event initialize
23675              * Fires when the editor is fully initialized (including the iframe)
23676              * @param {HtmlEditor} this
23677              */
23678             initialize: true,
23679             /**
23680              * @event activate
23681              * Fires when the editor is first receives the focus. Any insertion must wait
23682              * until after this event.
23683              * @param {HtmlEditor} this
23684              */
23685             activate: true,
23686              /**
23687              * @event beforesync
23688              * Fires before the textarea is updated with content from the editor iframe. Return false
23689              * to cancel the sync.
23690              * @param {HtmlEditor} this
23691              * @param {String} html
23692              */
23693             beforesync: true,
23694              /**
23695              * @event beforepush
23696              * Fires before the iframe editor is updated with content from the textarea. Return false
23697              * to cancel the push.
23698              * @param {HtmlEditor} this
23699              * @param {String} html
23700              */
23701             beforepush: true,
23702              /**
23703              * @event sync
23704              * Fires when the textarea is updated with content from the editor iframe.
23705              * @param {HtmlEditor} this
23706              * @param {String} html
23707              */
23708             sync: true,
23709              /**
23710              * @event push
23711              * Fires when the iframe editor is updated with content from the textarea.
23712              * @param {HtmlEditor} this
23713              * @param {String} html
23714              */
23715             push: true,
23716              /**
23717              * @event editmodechange
23718              * Fires when the editor switches edit modes
23719              * @param {HtmlEditor} this
23720              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23721              */
23722             editmodechange: true,
23723             /**
23724              * @event editorevent
23725              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23726              * @param {HtmlEditor} this
23727              */
23728             editorevent: true,
23729             /**
23730              * @event firstfocus
23731              * Fires when on first focus - needed by toolbars..
23732              * @param {HtmlEditor} this
23733              */
23734             firstfocus: true,
23735             /**
23736              * @event autosave
23737              * Auto save the htmlEditor value as a file into Events
23738              * @param {HtmlEditor} this
23739              */
23740             autosave: true,
23741             /**
23742              * @event savedpreview
23743              * preview the saved version of htmlEditor
23744              * @param {HtmlEditor} this
23745              */
23746             savedpreview: true
23747         });
23748 };
23749
23750
23751 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23752     
23753     
23754       /**
23755      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23756      */
23757     toolbars : false,
23758     
23759      /**
23760     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23761     */
23762     btns : [],
23763    
23764      /**
23765      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23766      *                        Roo.resizable.
23767      */
23768     resizable : false,
23769      /**
23770      * @cfg {Number} height (in pixels)
23771      */   
23772     height: 300,
23773    /**
23774      * @cfg {Number} width (in pixels)
23775      */   
23776     width: false,
23777     
23778     /**
23779      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23780      * 
23781      */
23782     stylesheets: false,
23783     
23784     // id of frame..
23785     frameId: false,
23786     
23787     // private properties
23788     validationEvent : false,
23789     deferHeight: true,
23790     initialized : false,
23791     activated : false,
23792     
23793     onFocus : Roo.emptyFn,
23794     iframePad:3,
23795     hideMode:'offsets',
23796     
23797     tbContainer : false,
23798     
23799     bodyCls : '',
23800     
23801     toolbarContainer :function() {
23802         return this.wrap.select('.x-html-editor-tb',true).first();
23803     },
23804
23805     /**
23806      * Protected method that will not generally be called directly. It
23807      * is called when the editor creates its toolbar. Override this method if you need to
23808      * add custom toolbar buttons.
23809      * @param {HtmlEditor} editor
23810      */
23811     createToolbar : function(){
23812         Roo.log('renewing');
23813         Roo.log("create toolbars");
23814         
23815         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23816         this.toolbars[0].render(this.toolbarContainer());
23817         
23818         return;
23819         
23820 //        if (!editor.toolbars || !editor.toolbars.length) {
23821 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23822 //        }
23823 //        
23824 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23825 //            editor.toolbars[i] = Roo.factory(
23826 //                    typeof(editor.toolbars[i]) == 'string' ?
23827 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23828 //                Roo.bootstrap.HtmlEditor);
23829 //            editor.toolbars[i].init(editor);
23830 //        }
23831     },
23832
23833      
23834     // private
23835     onRender : function(ct, position)
23836     {
23837        // Roo.log("Call onRender: " + this.xtype);
23838         var _t = this;
23839         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23840       
23841         this.wrap = this.inputEl().wrap({
23842             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23843         });
23844         
23845         this.editorcore.onRender(ct, position);
23846          
23847         if (this.resizable) {
23848             this.resizeEl = new Roo.Resizable(this.wrap, {
23849                 pinned : true,
23850                 wrap: true,
23851                 dynamic : true,
23852                 minHeight : this.height,
23853                 height: this.height,
23854                 handles : this.resizable,
23855                 width: this.width,
23856                 listeners : {
23857                     resize : function(r, w, h) {
23858                         _t.onResize(w,h); // -something
23859                     }
23860                 }
23861             });
23862             
23863         }
23864         this.createToolbar(this);
23865        
23866         
23867         if(!this.width && this.resizable){
23868             this.setSize(this.wrap.getSize());
23869         }
23870         if (this.resizeEl) {
23871             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23872             // should trigger onReize..
23873         }
23874         
23875     },
23876
23877     // private
23878     onResize : function(w, h)
23879     {
23880         Roo.log('resize: ' +w + ',' + h );
23881         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23882         var ew = false;
23883         var eh = false;
23884         
23885         if(this.inputEl() ){
23886             if(typeof w == 'number'){
23887                 var aw = w - this.wrap.getFrameWidth('lr');
23888                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23889                 ew = aw;
23890             }
23891             if(typeof h == 'number'){
23892                  var tbh = -11;  // fixme it needs to tool bar size!
23893                 for (var i =0; i < this.toolbars.length;i++) {
23894                     // fixme - ask toolbars for heights?
23895                     tbh += this.toolbars[i].el.getHeight();
23896                     //if (this.toolbars[i].footer) {
23897                     //    tbh += this.toolbars[i].footer.el.getHeight();
23898                     //}
23899                 }
23900               
23901                 
23902                 
23903                 
23904                 
23905                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23906                 ah -= 5; // knock a few pixes off for look..
23907                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23908                 var eh = ah;
23909             }
23910         }
23911         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23912         this.editorcore.onResize(ew,eh);
23913         
23914     },
23915
23916     /**
23917      * Toggles the editor between standard and source edit mode.
23918      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23919      */
23920     toggleSourceEdit : function(sourceEditMode)
23921     {
23922         this.editorcore.toggleSourceEdit(sourceEditMode);
23923         
23924         if(this.editorcore.sourceEditMode){
23925             Roo.log('editor - showing textarea');
23926             
23927 //            Roo.log('in');
23928 //            Roo.log(this.syncValue());
23929             this.syncValue();
23930             this.inputEl().removeClass(['hide', 'x-hidden']);
23931             this.inputEl().dom.removeAttribute('tabIndex');
23932             this.inputEl().focus();
23933         }else{
23934             Roo.log('editor - hiding textarea');
23935 //            Roo.log('out')
23936 //            Roo.log(this.pushValue()); 
23937             this.pushValue();
23938             
23939             this.inputEl().addClass(['hide', 'x-hidden']);
23940             this.inputEl().dom.setAttribute('tabIndex', -1);
23941             //this.deferFocus();
23942         }
23943          
23944         if(this.resizable){
23945             this.setSize(this.wrap.getSize());
23946         }
23947         
23948         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23949     },
23950  
23951     // private (for BoxComponent)
23952     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23953
23954     // private (for BoxComponent)
23955     getResizeEl : function(){
23956         return this.wrap;
23957     },
23958
23959     // private (for BoxComponent)
23960     getPositionEl : function(){
23961         return this.wrap;
23962     },
23963
23964     // private
23965     initEvents : function(){
23966         this.originalValue = this.getValue();
23967     },
23968
23969 //    /**
23970 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23971 //     * @method
23972 //     */
23973 //    markInvalid : Roo.emptyFn,
23974 //    /**
23975 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23976 //     * @method
23977 //     */
23978 //    clearInvalid : Roo.emptyFn,
23979
23980     setValue : function(v){
23981         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23982         this.editorcore.pushValue();
23983     },
23984
23985      
23986     // private
23987     deferFocus : function(){
23988         this.focus.defer(10, this);
23989     },
23990
23991     // doc'ed in Field
23992     focus : function(){
23993         this.editorcore.focus();
23994         
23995     },
23996       
23997
23998     // private
23999     onDestroy : function(){
24000         
24001         
24002         
24003         if(this.rendered){
24004             
24005             for (var i =0; i < this.toolbars.length;i++) {
24006                 // fixme - ask toolbars for heights?
24007                 this.toolbars[i].onDestroy();
24008             }
24009             
24010             this.wrap.dom.innerHTML = '';
24011             this.wrap.remove();
24012         }
24013     },
24014
24015     // private
24016     onFirstFocus : function(){
24017         //Roo.log("onFirstFocus");
24018         this.editorcore.onFirstFocus();
24019          for (var i =0; i < this.toolbars.length;i++) {
24020             this.toolbars[i].onFirstFocus();
24021         }
24022         
24023     },
24024     
24025     // private
24026     syncValue : function()
24027     {   
24028         this.editorcore.syncValue();
24029     },
24030     
24031     pushValue : function()
24032     {   
24033         this.editorcore.pushValue();
24034     }
24035      
24036     
24037     // hide stuff that is not compatible
24038     /**
24039      * @event blur
24040      * @hide
24041      */
24042     /**
24043      * @event change
24044      * @hide
24045      */
24046     /**
24047      * @event focus
24048      * @hide
24049      */
24050     /**
24051      * @event specialkey
24052      * @hide
24053      */
24054     /**
24055      * @cfg {String} fieldClass @hide
24056      */
24057     /**
24058      * @cfg {String} focusClass @hide
24059      */
24060     /**
24061      * @cfg {String} autoCreate @hide
24062      */
24063     /**
24064      * @cfg {String} inputType @hide
24065      */
24066      
24067     /**
24068      * @cfg {String} invalidText @hide
24069      */
24070     /**
24071      * @cfg {String} msgFx @hide
24072      */
24073     /**
24074      * @cfg {String} validateOnBlur @hide
24075      */
24076 });
24077  
24078     
24079    
24080    
24081    
24082       
24083 Roo.namespace('Roo.bootstrap.htmleditor');
24084 /**
24085  * @class Roo.bootstrap.HtmlEditorToolbar1
24086  * Basic Toolbar
24087  * 
24088  * @example
24089  * Usage:
24090  *
24091  new Roo.bootstrap.HtmlEditor({
24092     ....
24093     toolbars : [
24094         new Roo.bootstrap.HtmlEditorToolbar1({
24095             disable : { fonts: 1 , format: 1, ..., ... , ...],
24096             btns : [ .... ]
24097         })
24098     }
24099      
24100  * 
24101  * @cfg {Object} disable List of elements to disable..
24102  * @cfg {Array} btns List of additional buttons.
24103  * 
24104  * 
24105  * NEEDS Extra CSS? 
24106  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24107  */
24108  
24109 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24110 {
24111     
24112     Roo.apply(this, config);
24113     
24114     // default disabled, based on 'good practice'..
24115     this.disable = this.disable || {};
24116     Roo.applyIf(this.disable, {
24117         fontSize : true,
24118         colors : true,
24119         specialElements : true
24120     });
24121     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24122     
24123     this.editor = config.editor;
24124     this.editorcore = config.editor.editorcore;
24125     
24126     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24127     
24128     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24129     // dont call parent... till later.
24130 }
24131 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24132      
24133     bar : true,
24134     
24135     editor : false,
24136     editorcore : false,
24137     
24138     
24139     formats : [
24140         "p" ,  
24141         "h1","h2","h3","h4","h5","h6", 
24142         "pre", "code", 
24143         "abbr", "acronym", "address", "cite", "samp", "var",
24144         'div','span'
24145     ],
24146     
24147     onRender : function(ct, position)
24148     {
24149        // Roo.log("Call onRender: " + this.xtype);
24150         
24151        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24152        Roo.log(this.el);
24153        this.el.dom.style.marginBottom = '0';
24154        var _this = this;
24155        var editorcore = this.editorcore;
24156        var editor= this.editor;
24157        
24158        var children = [];
24159        var btn = function(id,cmd , toggle, handler, html){
24160        
24161             var  event = toggle ? 'toggle' : 'click';
24162        
24163             var a = {
24164                 size : 'sm',
24165                 xtype: 'Button',
24166                 xns: Roo.bootstrap,
24167                 //glyphicon : id,
24168                 fa: id,
24169                 cmd : id || cmd,
24170                 enableToggle:toggle !== false,
24171                 html : html || '',
24172                 pressed : toggle ? false : null,
24173                 listeners : {}
24174             };
24175             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24176                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24177             };
24178             children.push(a);
24179             return a;
24180        }
24181        
24182     //    var cb_box = function...
24183         
24184         var style = {
24185                 xtype: 'Button',
24186                 size : 'sm',
24187                 xns: Roo.bootstrap,
24188                 fa : 'font',
24189                 //html : 'submit'
24190                 menu : {
24191                     xtype: 'Menu',
24192                     xns: Roo.bootstrap,
24193                     items:  []
24194                 }
24195         };
24196         Roo.each(this.formats, function(f) {
24197             style.menu.items.push({
24198                 xtype :'MenuItem',
24199                 xns: Roo.bootstrap,
24200                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24201                 tagname : f,
24202                 listeners : {
24203                     click : function()
24204                     {
24205                         editorcore.insertTag(this.tagname);
24206                         editor.focus();
24207                     }
24208                 }
24209                 
24210             });
24211         });
24212         children.push(style);   
24213         
24214         btn('bold',false,true);
24215         btn('italic',false,true);
24216         btn('align-left', 'justifyleft',true);
24217         btn('align-center', 'justifycenter',true);
24218         btn('align-right' , 'justifyright',true);
24219         btn('link', false, false, function(btn) {
24220             //Roo.log("create link?");
24221             var url = prompt(this.createLinkText, this.defaultLinkValue);
24222             if(url && url != 'http:/'+'/'){
24223                 this.editorcore.relayCmd('createlink', url);
24224             }
24225         }),
24226         btn('list','insertunorderedlist',true);
24227         btn('pencil', false,true, function(btn){
24228                 Roo.log(this);
24229                 this.toggleSourceEdit(btn.pressed);
24230         });
24231         
24232         if (this.editor.btns.length > 0) {
24233             for (var i = 0; i<this.editor.btns.length; i++) {
24234                 children.push(this.editor.btns[i]);
24235             }
24236         }
24237         
24238         /*
24239         var cog = {
24240                 xtype: 'Button',
24241                 size : 'sm',
24242                 xns: Roo.bootstrap,
24243                 glyphicon : 'cog',
24244                 //html : 'submit'
24245                 menu : {
24246                     xtype: 'Menu',
24247                     xns: Roo.bootstrap,
24248                     items:  []
24249                 }
24250         };
24251         
24252         cog.menu.items.push({
24253             xtype :'MenuItem',
24254             xns: Roo.bootstrap,
24255             html : Clean styles,
24256             tagname : f,
24257             listeners : {
24258                 click : function()
24259                 {
24260                     editorcore.insertTag(this.tagname);
24261                     editor.focus();
24262                 }
24263             }
24264             
24265         });
24266        */
24267         
24268          
24269        this.xtype = 'NavSimplebar';
24270         
24271         for(var i=0;i< children.length;i++) {
24272             
24273             this.buttons.add(this.addxtypeChild(children[i]));
24274             
24275         }
24276         
24277         editor.on('editorevent', this.updateToolbar, this);
24278     },
24279     onBtnClick : function(id)
24280     {
24281        this.editorcore.relayCmd(id);
24282        this.editorcore.focus();
24283     },
24284     
24285     /**
24286      * Protected method that will not generally be called directly. It triggers
24287      * a toolbar update by reading the markup state of the current selection in the editor.
24288      */
24289     updateToolbar: function(){
24290
24291         if(!this.editorcore.activated){
24292             this.editor.onFirstFocus(); // is this neeed?
24293             return;
24294         }
24295
24296         var btns = this.buttons; 
24297         var doc = this.editorcore.doc;
24298         btns.get('bold').setActive(doc.queryCommandState('bold'));
24299         btns.get('italic').setActive(doc.queryCommandState('italic'));
24300         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24301         
24302         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24303         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24304         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24305         
24306         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24307         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24308          /*
24309         
24310         var ans = this.editorcore.getAllAncestors();
24311         if (this.formatCombo) {
24312             
24313             
24314             var store = this.formatCombo.store;
24315             this.formatCombo.setValue("");
24316             for (var i =0; i < ans.length;i++) {
24317                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24318                     // select it..
24319                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24320                     break;
24321                 }
24322             }
24323         }
24324         
24325         
24326         
24327         // hides menus... - so this cant be on a menu...
24328         Roo.bootstrap.MenuMgr.hideAll();
24329         */
24330         Roo.bootstrap.MenuMgr.hideAll();
24331         //this.editorsyncValue();
24332     },
24333     onFirstFocus: function() {
24334         this.buttons.each(function(item){
24335            item.enable();
24336         });
24337     },
24338     toggleSourceEdit : function(sourceEditMode){
24339         
24340           
24341         if(sourceEditMode){
24342             Roo.log("disabling buttons");
24343            this.buttons.each( function(item){
24344                 if(item.cmd != 'pencil'){
24345                     item.disable();
24346                 }
24347             });
24348           
24349         }else{
24350             Roo.log("enabling buttons");
24351             if(this.editorcore.initialized){
24352                 this.buttons.each( function(item){
24353                     item.enable();
24354                 });
24355             }
24356             
24357         }
24358         Roo.log("calling toggole on editor");
24359         // tell the editor that it's been pressed..
24360         this.editor.toggleSourceEdit(sourceEditMode);
24361        
24362     }
24363 });
24364
24365
24366
24367
24368
24369 /**
24370  * @class Roo.bootstrap.Table.AbstractSelectionModel
24371  * @extends Roo.util.Observable
24372  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24373  * implemented by descendant classes.  This class should not be directly instantiated.
24374  * @constructor
24375  */
24376 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24377     this.locked = false;
24378     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24379 };
24380
24381
24382 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24383     /** @ignore Called by the grid automatically. Do not call directly. */
24384     init : function(grid){
24385         this.grid = grid;
24386         this.initEvents();
24387     },
24388
24389     /**
24390      * Locks the selections.
24391      */
24392     lock : function(){
24393         this.locked = true;
24394     },
24395
24396     /**
24397      * Unlocks the selections.
24398      */
24399     unlock : function(){
24400         this.locked = false;
24401     },
24402
24403     /**
24404      * Returns true if the selections are locked.
24405      * @return {Boolean}
24406      */
24407     isLocked : function(){
24408         return this.locked;
24409     }
24410 });
24411 /**
24412  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24413  * @class Roo.bootstrap.Table.RowSelectionModel
24414  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24415  * It supports multiple selections and keyboard selection/navigation. 
24416  * @constructor
24417  * @param {Object} config
24418  */
24419
24420 Roo.bootstrap.Table.RowSelectionModel = function(config){
24421     Roo.apply(this, config);
24422     this.selections = new Roo.util.MixedCollection(false, function(o){
24423         return o.id;
24424     });
24425
24426     this.last = false;
24427     this.lastActive = false;
24428
24429     this.addEvents({
24430         /**
24431              * @event selectionchange
24432              * Fires when the selection changes
24433              * @param {SelectionModel} this
24434              */
24435             "selectionchange" : true,
24436         /**
24437              * @event afterselectionchange
24438              * Fires after the selection changes (eg. by key press or clicking)
24439              * @param {SelectionModel} this
24440              */
24441             "afterselectionchange" : true,
24442         /**
24443              * @event beforerowselect
24444              * Fires when a row is selected being selected, return false to cancel.
24445              * @param {SelectionModel} this
24446              * @param {Number} rowIndex The selected index
24447              * @param {Boolean} keepExisting False if other selections will be cleared
24448              */
24449             "beforerowselect" : true,
24450         /**
24451              * @event rowselect
24452              * Fires when a row is selected.
24453              * @param {SelectionModel} this
24454              * @param {Number} rowIndex The selected index
24455              * @param {Roo.data.Record} r The record
24456              */
24457             "rowselect" : true,
24458         /**
24459              * @event rowdeselect
24460              * Fires when a row is deselected.
24461              * @param {SelectionModel} this
24462              * @param {Number} rowIndex The selected index
24463              */
24464         "rowdeselect" : true
24465     });
24466     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24467     this.locked = false;
24468  };
24469
24470 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24471     /**
24472      * @cfg {Boolean} singleSelect
24473      * True to allow selection of only one row at a time (defaults to false)
24474      */
24475     singleSelect : false,
24476
24477     // private
24478     initEvents : function()
24479     {
24480
24481         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24482         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24483         //}else{ // allow click to work like normal
24484          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24485         //}
24486         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24487         this.grid.on("rowclick", this.handleMouseDown, this);
24488         
24489         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24490             "up" : function(e){
24491                 if(!e.shiftKey){
24492                     this.selectPrevious(e.shiftKey);
24493                 }else if(this.last !== false && this.lastActive !== false){
24494                     var last = this.last;
24495                     this.selectRange(this.last,  this.lastActive-1);
24496                     this.grid.getView().focusRow(this.lastActive);
24497                     if(last !== false){
24498                         this.last = last;
24499                     }
24500                 }else{
24501                     this.selectFirstRow();
24502                 }
24503                 this.fireEvent("afterselectionchange", this);
24504             },
24505             "down" : function(e){
24506                 if(!e.shiftKey){
24507                     this.selectNext(e.shiftKey);
24508                 }else if(this.last !== false && this.lastActive !== false){
24509                     var last = this.last;
24510                     this.selectRange(this.last,  this.lastActive+1);
24511                     this.grid.getView().focusRow(this.lastActive);
24512                     if(last !== false){
24513                         this.last = last;
24514                     }
24515                 }else{
24516                     this.selectFirstRow();
24517                 }
24518                 this.fireEvent("afterselectionchange", this);
24519             },
24520             scope: this
24521         });
24522         this.grid.store.on('load', function(){
24523             this.selections.clear();
24524         },this);
24525         /*
24526         var view = this.grid.view;
24527         view.on("refresh", this.onRefresh, this);
24528         view.on("rowupdated", this.onRowUpdated, this);
24529         view.on("rowremoved", this.onRemove, this);
24530         */
24531     },
24532
24533     // private
24534     onRefresh : function()
24535     {
24536         var ds = this.grid.store, i, v = this.grid.view;
24537         var s = this.selections;
24538         s.each(function(r){
24539             if((i = ds.indexOfId(r.id)) != -1){
24540                 v.onRowSelect(i);
24541             }else{
24542                 s.remove(r);
24543             }
24544         });
24545     },
24546
24547     // private
24548     onRemove : function(v, index, r){
24549         this.selections.remove(r);
24550     },
24551
24552     // private
24553     onRowUpdated : function(v, index, r){
24554         if(this.isSelected(r)){
24555             v.onRowSelect(index);
24556         }
24557     },
24558
24559     /**
24560      * Select records.
24561      * @param {Array} records The records to select
24562      * @param {Boolean} keepExisting (optional) True to keep existing selections
24563      */
24564     selectRecords : function(records, keepExisting)
24565     {
24566         if(!keepExisting){
24567             this.clearSelections();
24568         }
24569             var ds = this.grid.store;
24570         for(var i = 0, len = records.length; i < len; i++){
24571             this.selectRow(ds.indexOf(records[i]), true);
24572         }
24573     },
24574
24575     /**
24576      * Gets the number of selected rows.
24577      * @return {Number}
24578      */
24579     getCount : function(){
24580         return this.selections.length;
24581     },
24582
24583     /**
24584      * Selects the first row in the grid.
24585      */
24586     selectFirstRow : function(){
24587         this.selectRow(0);
24588     },
24589
24590     /**
24591      * Select the last row.
24592      * @param {Boolean} keepExisting (optional) True to keep existing selections
24593      */
24594     selectLastRow : function(keepExisting){
24595         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24596         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24597     },
24598
24599     /**
24600      * Selects the row immediately following the last selected row.
24601      * @param {Boolean} keepExisting (optional) True to keep existing selections
24602      */
24603     selectNext : function(keepExisting)
24604     {
24605             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24606             this.selectRow(this.last+1, keepExisting);
24607             this.grid.getView().focusRow(this.last);
24608         }
24609     },
24610
24611     /**
24612      * Selects the row that precedes the last selected row.
24613      * @param {Boolean} keepExisting (optional) True to keep existing selections
24614      */
24615     selectPrevious : function(keepExisting){
24616         if(this.last){
24617             this.selectRow(this.last-1, keepExisting);
24618             this.grid.getView().focusRow(this.last);
24619         }
24620     },
24621
24622     /**
24623      * Returns the selected records
24624      * @return {Array} Array of selected records
24625      */
24626     getSelections : function(){
24627         return [].concat(this.selections.items);
24628     },
24629
24630     /**
24631      * Returns the first selected record.
24632      * @return {Record}
24633      */
24634     getSelected : function(){
24635         return this.selections.itemAt(0);
24636     },
24637
24638
24639     /**
24640      * Clears all selections.
24641      */
24642     clearSelections : function(fast)
24643     {
24644         if(this.locked) {
24645             return;
24646         }
24647         if(fast !== true){
24648                 var ds = this.grid.store;
24649             var s = this.selections;
24650             s.each(function(r){
24651                 this.deselectRow(ds.indexOfId(r.id));
24652             }, this);
24653             s.clear();
24654         }else{
24655             this.selections.clear();
24656         }
24657         this.last = false;
24658     },
24659
24660
24661     /**
24662      * Selects all rows.
24663      */
24664     selectAll : function(){
24665         if(this.locked) {
24666             return;
24667         }
24668         this.selections.clear();
24669         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24670             this.selectRow(i, true);
24671         }
24672     },
24673
24674     /**
24675      * Returns True if there is a selection.
24676      * @return {Boolean}
24677      */
24678     hasSelection : function(){
24679         return this.selections.length > 0;
24680     },
24681
24682     /**
24683      * Returns True if the specified row is selected.
24684      * @param {Number/Record} record The record or index of the record to check
24685      * @return {Boolean}
24686      */
24687     isSelected : function(index){
24688             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24689         return (r && this.selections.key(r.id) ? true : false);
24690     },
24691
24692     /**
24693      * Returns True if the specified record id is selected.
24694      * @param {String} id The id of record to check
24695      * @return {Boolean}
24696      */
24697     isIdSelected : function(id){
24698         return (this.selections.key(id) ? true : false);
24699     },
24700
24701
24702     // private
24703     handleMouseDBClick : function(e, t){
24704         
24705     },
24706     // private
24707     handleMouseDown : function(e, t)
24708     {
24709             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24710         if(this.isLocked() || rowIndex < 0 ){
24711             return;
24712         };
24713         if(e.shiftKey && this.last !== false){
24714             var last = this.last;
24715             this.selectRange(last, rowIndex, e.ctrlKey);
24716             this.last = last; // reset the last
24717             t.focus();
24718     
24719         }else{
24720             var isSelected = this.isSelected(rowIndex);
24721             //Roo.log("select row:" + rowIndex);
24722             if(isSelected){
24723                 this.deselectRow(rowIndex);
24724             } else {
24725                         this.selectRow(rowIndex, true);
24726             }
24727     
24728             /*
24729                 if(e.button !== 0 && isSelected){
24730                 alert('rowIndex 2: ' + rowIndex);
24731                     view.focusRow(rowIndex);
24732                 }else if(e.ctrlKey && isSelected){
24733                     this.deselectRow(rowIndex);
24734                 }else if(!isSelected){
24735                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24736                     view.focusRow(rowIndex);
24737                 }
24738             */
24739         }
24740         this.fireEvent("afterselectionchange", this);
24741     },
24742     // private
24743     handleDragableRowClick :  function(grid, rowIndex, e) 
24744     {
24745         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24746             this.selectRow(rowIndex, false);
24747             grid.view.focusRow(rowIndex);
24748              this.fireEvent("afterselectionchange", this);
24749         }
24750     },
24751     
24752     /**
24753      * Selects multiple rows.
24754      * @param {Array} rows Array of the indexes of the row to select
24755      * @param {Boolean} keepExisting (optional) True to keep existing selections
24756      */
24757     selectRows : function(rows, keepExisting){
24758         if(!keepExisting){
24759             this.clearSelections();
24760         }
24761         for(var i = 0, len = rows.length; i < len; i++){
24762             this.selectRow(rows[i], true);
24763         }
24764     },
24765
24766     /**
24767      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24768      * @param {Number} startRow The index of the first row in the range
24769      * @param {Number} endRow The index of the last row in the range
24770      * @param {Boolean} keepExisting (optional) True to retain existing selections
24771      */
24772     selectRange : function(startRow, endRow, keepExisting){
24773         if(this.locked) {
24774             return;
24775         }
24776         if(!keepExisting){
24777             this.clearSelections();
24778         }
24779         if(startRow <= endRow){
24780             for(var i = startRow; i <= endRow; i++){
24781                 this.selectRow(i, true);
24782             }
24783         }else{
24784             for(var i = startRow; i >= endRow; i--){
24785                 this.selectRow(i, true);
24786             }
24787         }
24788     },
24789
24790     /**
24791      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24792      * @param {Number} startRow The index of the first row in the range
24793      * @param {Number} endRow The index of the last row in the range
24794      */
24795     deselectRange : function(startRow, endRow, preventViewNotify){
24796         if(this.locked) {
24797             return;
24798         }
24799         for(var i = startRow; i <= endRow; i++){
24800             this.deselectRow(i, preventViewNotify);
24801         }
24802     },
24803
24804     /**
24805      * Selects a row.
24806      * @param {Number} row The index of the row to select
24807      * @param {Boolean} keepExisting (optional) True to keep existing selections
24808      */
24809     selectRow : function(index, keepExisting, preventViewNotify)
24810     {
24811             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24812             return;
24813         }
24814         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24815             if(!keepExisting || this.singleSelect){
24816                 this.clearSelections();
24817             }
24818             
24819             var r = this.grid.store.getAt(index);
24820             //console.log('selectRow - record id :' + r.id);
24821             
24822             this.selections.add(r);
24823             this.last = this.lastActive = index;
24824             if(!preventViewNotify){
24825                 var proxy = new Roo.Element(
24826                                 this.grid.getRowDom(index)
24827                 );
24828                 proxy.addClass('bg-info info');
24829             }
24830             this.fireEvent("rowselect", this, index, r);
24831             this.fireEvent("selectionchange", this);
24832         }
24833     },
24834
24835     /**
24836      * Deselects a row.
24837      * @param {Number} row The index of the row to deselect
24838      */
24839     deselectRow : function(index, preventViewNotify)
24840     {
24841         if(this.locked) {
24842             return;
24843         }
24844         if(this.last == index){
24845             this.last = false;
24846         }
24847         if(this.lastActive == index){
24848             this.lastActive = false;
24849         }
24850         
24851         var r = this.grid.store.getAt(index);
24852         if (!r) {
24853             return;
24854         }
24855         
24856         this.selections.remove(r);
24857         //.console.log('deselectRow - record id :' + r.id);
24858         if(!preventViewNotify){
24859         
24860             var proxy = new Roo.Element(
24861                 this.grid.getRowDom(index)
24862             );
24863             proxy.removeClass('bg-info info');
24864         }
24865         this.fireEvent("rowdeselect", this, index);
24866         this.fireEvent("selectionchange", this);
24867     },
24868
24869     // private
24870     restoreLast : function(){
24871         if(this._last){
24872             this.last = this._last;
24873         }
24874     },
24875
24876     // private
24877     acceptsNav : function(row, col, cm){
24878         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24879     },
24880
24881     // private
24882     onEditorKey : function(field, e){
24883         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24884         if(k == e.TAB){
24885             e.stopEvent();
24886             ed.completeEdit();
24887             if(e.shiftKey){
24888                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24889             }else{
24890                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24891             }
24892         }else if(k == e.ENTER && !e.ctrlKey){
24893             e.stopEvent();
24894             ed.completeEdit();
24895             if(e.shiftKey){
24896                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24897             }else{
24898                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24899             }
24900         }else if(k == e.ESC){
24901             ed.cancelEdit();
24902         }
24903         if(newCell){
24904             g.startEditing(newCell[0], newCell[1]);
24905         }
24906     }
24907 });
24908 /*
24909  * Based on:
24910  * Ext JS Library 1.1.1
24911  * Copyright(c) 2006-2007, Ext JS, LLC.
24912  *
24913  * Originally Released Under LGPL - original licence link has changed is not relivant.
24914  *
24915  * Fork - LGPL
24916  * <script type="text/javascript">
24917  */
24918  
24919 /**
24920  * @class Roo.bootstrap.PagingToolbar
24921  * @extends Roo.bootstrap.NavSimplebar
24922  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24923  * @constructor
24924  * Create a new PagingToolbar
24925  * @param {Object} config The config object
24926  * @param {Roo.data.Store} store
24927  */
24928 Roo.bootstrap.PagingToolbar = function(config)
24929 {
24930     // old args format still supported... - xtype is prefered..
24931         // created from xtype...
24932     
24933     this.ds = config.dataSource;
24934     
24935     if (config.store && !this.ds) {
24936         this.store= Roo.factory(config.store, Roo.data);
24937         this.ds = this.store;
24938         this.ds.xmodule = this.xmodule || false;
24939     }
24940     
24941     this.toolbarItems = [];
24942     if (config.items) {
24943         this.toolbarItems = config.items;
24944     }
24945     
24946     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24947     
24948     this.cursor = 0;
24949     
24950     if (this.ds) { 
24951         this.bind(this.ds);
24952     }
24953     
24954     if (Roo.bootstrap.version == 4) {
24955         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24956     } else {
24957         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24958     }
24959     
24960 };
24961
24962 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24963     /**
24964      * @cfg {Roo.data.Store} dataSource
24965      * The underlying data store providing the paged data
24966      */
24967     /**
24968      * @cfg {String/HTMLElement/Element} container
24969      * container The id or element that will contain the toolbar
24970      */
24971     /**
24972      * @cfg {Boolean} displayInfo
24973      * True to display the displayMsg (defaults to false)
24974      */
24975     /**
24976      * @cfg {Number} pageSize
24977      * The number of records to display per page (defaults to 20)
24978      */
24979     pageSize: 20,
24980     /**
24981      * @cfg {String} displayMsg
24982      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24983      */
24984     displayMsg : 'Displaying {0} - {1} of {2}',
24985     /**
24986      * @cfg {String} emptyMsg
24987      * The message to display when no records are found (defaults to "No data to display")
24988      */
24989     emptyMsg : 'No data to display',
24990     /**
24991      * Customizable piece of the default paging text (defaults to "Page")
24992      * @type String
24993      */
24994     beforePageText : "Page",
24995     /**
24996      * Customizable piece of the default paging text (defaults to "of %0")
24997      * @type String
24998      */
24999     afterPageText : "of {0}",
25000     /**
25001      * Customizable piece of the default paging text (defaults to "First Page")
25002      * @type String
25003      */
25004     firstText : "First Page",
25005     /**
25006      * Customizable piece of the default paging text (defaults to "Previous Page")
25007      * @type String
25008      */
25009     prevText : "Previous Page",
25010     /**
25011      * Customizable piece of the default paging text (defaults to "Next Page")
25012      * @type String
25013      */
25014     nextText : "Next Page",
25015     /**
25016      * Customizable piece of the default paging text (defaults to "Last Page")
25017      * @type String
25018      */
25019     lastText : "Last Page",
25020     /**
25021      * Customizable piece of the default paging text (defaults to "Refresh")
25022      * @type String
25023      */
25024     refreshText : "Refresh",
25025
25026     buttons : false,
25027     // private
25028     onRender : function(ct, position) 
25029     {
25030         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
25031         this.navgroup.parentId = this.id;
25032         this.navgroup.onRender(this.el, null);
25033         // add the buttons to the navgroup
25034         
25035         if(this.displayInfo){
25036             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
25037             this.displayEl = this.el.select('.x-paging-info', true).first();
25038 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
25039 //            this.displayEl = navel.el.select('span',true).first();
25040         }
25041         
25042         var _this = this;
25043         
25044         if(this.buttons){
25045             Roo.each(_this.buttons, function(e){ // this might need to use render????
25046                Roo.factory(e).render(_this.el);
25047             });
25048         }
25049             
25050         Roo.each(_this.toolbarItems, function(e) {
25051             _this.navgroup.addItem(e);
25052         });
25053         
25054         
25055         this.first = this.navgroup.addItem({
25056             tooltip: this.firstText,
25057             cls: "prev btn-outline-secondary",
25058             html : ' <i class="fa fa-step-backward"></i>',
25059             disabled: true,
25060             preventDefault: true,
25061             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25062         });
25063         
25064         this.prev =  this.navgroup.addItem({
25065             tooltip: this.prevText,
25066             cls: "prev btn-outline-secondary",
25067             html : ' <i class="fa fa-backward"></i>',
25068             disabled: true,
25069             preventDefault: true,
25070             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25071         });
25072     //this.addSeparator();
25073         
25074         
25075         var field = this.navgroup.addItem( {
25076             tagtype : 'span',
25077             cls : 'x-paging-position  btn-outline-secondary',
25078              disabled: true,
25079             html : this.beforePageText  +
25080                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25081                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25082          } ); //?? escaped?
25083         
25084         this.field = field.el.select('input', true).first();
25085         this.field.on("keydown", this.onPagingKeydown, this);
25086         this.field.on("focus", function(){this.dom.select();});
25087     
25088     
25089         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25090         //this.field.setHeight(18);
25091         //this.addSeparator();
25092         this.next = this.navgroup.addItem({
25093             tooltip: this.nextText,
25094             cls: "next btn-outline-secondary",
25095             html : ' <i class="fa fa-forward"></i>',
25096             disabled: true,
25097             preventDefault: true,
25098             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25099         });
25100         this.last = this.navgroup.addItem({
25101             tooltip: this.lastText,
25102             html : ' <i class="fa fa-step-forward"></i>',
25103             cls: "next btn-outline-secondary",
25104             disabled: true,
25105             preventDefault: true,
25106             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25107         });
25108     //this.addSeparator();
25109         this.loading = this.navgroup.addItem({
25110             tooltip: this.refreshText,
25111             cls: "btn-outline-secondary",
25112             html : ' <i class="fa fa-refresh"></i>',
25113             preventDefault: true,
25114             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25115         });
25116         
25117     },
25118
25119     // private
25120     updateInfo : function(){
25121         if(this.displayEl){
25122             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25123             var msg = count == 0 ?
25124                 this.emptyMsg :
25125                 String.format(
25126                     this.displayMsg,
25127                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25128                 );
25129             this.displayEl.update(msg);
25130         }
25131     },
25132
25133     // private
25134     onLoad : function(ds, r, o)
25135     {
25136         this.cursor = o.params.start ? o.params.start : 0;
25137         
25138         var d = this.getPageData(),
25139             ap = d.activePage,
25140             ps = d.pages;
25141         
25142         
25143         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25144         this.field.dom.value = ap;
25145         this.first.setDisabled(ap == 1);
25146         this.prev.setDisabled(ap == 1);
25147         this.next.setDisabled(ap == ps);
25148         this.last.setDisabled(ap == ps);
25149         this.loading.enable();
25150         this.updateInfo();
25151     },
25152
25153     // private
25154     getPageData : function(){
25155         var total = this.ds.getTotalCount();
25156         return {
25157             total : total,
25158             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25159             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25160         };
25161     },
25162
25163     // private
25164     onLoadError : function(){
25165         this.loading.enable();
25166     },
25167
25168     // private
25169     onPagingKeydown : function(e){
25170         var k = e.getKey();
25171         var d = this.getPageData();
25172         if(k == e.RETURN){
25173             var v = this.field.dom.value, pageNum;
25174             if(!v || isNaN(pageNum = parseInt(v, 10))){
25175                 this.field.dom.value = d.activePage;
25176                 return;
25177             }
25178             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25179             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25180             e.stopEvent();
25181         }
25182         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
25183         {
25184           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25185           this.field.dom.value = pageNum;
25186           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25187           e.stopEvent();
25188         }
25189         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25190         {
25191           var v = this.field.dom.value, pageNum; 
25192           var increment = (e.shiftKey) ? 10 : 1;
25193           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25194                 increment *= -1;
25195           }
25196           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25197             this.field.dom.value = d.activePage;
25198             return;
25199           }
25200           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25201           {
25202             this.field.dom.value = parseInt(v, 10) + increment;
25203             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25204             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25205           }
25206           e.stopEvent();
25207         }
25208     },
25209
25210     // private
25211     beforeLoad : function(){
25212         if(this.loading){
25213             this.loading.disable();
25214         }
25215     },
25216
25217     // private
25218     onClick : function(which){
25219         
25220         var ds = this.ds;
25221         if (!ds) {
25222             return;
25223         }
25224         
25225         switch(which){
25226             case "first":
25227                 ds.load({params:{start: 0, limit: this.pageSize}});
25228             break;
25229             case "prev":
25230                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25231             break;
25232             case "next":
25233                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25234             break;
25235             case "last":
25236                 var total = ds.getTotalCount();
25237                 var extra = total % this.pageSize;
25238                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25239                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25240             break;
25241             case "refresh":
25242                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25243             break;
25244         }
25245     },
25246
25247     /**
25248      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25249      * @param {Roo.data.Store} store The data store to unbind
25250      */
25251     unbind : function(ds){
25252         ds.un("beforeload", this.beforeLoad, this);
25253         ds.un("load", this.onLoad, this);
25254         ds.un("loadexception", this.onLoadError, this);
25255         ds.un("remove", this.updateInfo, this);
25256         ds.un("add", this.updateInfo, this);
25257         this.ds = undefined;
25258     },
25259
25260     /**
25261      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25262      * @param {Roo.data.Store} store The data store to bind
25263      */
25264     bind : function(ds){
25265         ds.on("beforeload", this.beforeLoad, this);
25266         ds.on("load", this.onLoad, this);
25267         ds.on("loadexception", this.onLoadError, this);
25268         ds.on("remove", this.updateInfo, this);
25269         ds.on("add", this.updateInfo, this);
25270         this.ds = ds;
25271     }
25272 });/*
25273  * - LGPL
25274  *
25275  * element
25276  * 
25277  */
25278
25279 /**
25280  * @class Roo.bootstrap.MessageBar
25281  * @extends Roo.bootstrap.Component
25282  * Bootstrap MessageBar class
25283  * @cfg {String} html contents of the MessageBar
25284  * @cfg {String} weight (info | success | warning | danger) default info
25285  * @cfg {String} beforeClass insert the bar before the given class
25286  * @cfg {Boolean} closable (true | false) default false
25287  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25288  * 
25289  * @constructor
25290  * Create a new Element
25291  * @param {Object} config The config object
25292  */
25293
25294 Roo.bootstrap.MessageBar = function(config){
25295     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25296 };
25297
25298 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25299     
25300     html: '',
25301     weight: 'info',
25302     closable: false,
25303     fixed: false,
25304     beforeClass: 'bootstrap-sticky-wrap',
25305     
25306     getAutoCreate : function(){
25307         
25308         var cfg = {
25309             tag: 'div',
25310             cls: 'alert alert-dismissable alert-' + this.weight,
25311             cn: [
25312                 {
25313                     tag: 'span',
25314                     cls: 'message',
25315                     html: this.html || ''
25316                 }
25317             ]
25318         };
25319         
25320         if(this.fixed){
25321             cfg.cls += ' alert-messages-fixed';
25322         }
25323         
25324         if(this.closable){
25325             cfg.cn.push({
25326                 tag: 'button',
25327                 cls: 'close',
25328                 html: 'x'
25329             });
25330         }
25331         
25332         return cfg;
25333     },
25334     
25335     onRender : function(ct, position)
25336     {
25337         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25338         
25339         if(!this.el){
25340             var cfg = Roo.apply({},  this.getAutoCreate());
25341             cfg.id = Roo.id();
25342             
25343             if (this.cls) {
25344                 cfg.cls += ' ' + this.cls;
25345             }
25346             if (this.style) {
25347                 cfg.style = this.style;
25348             }
25349             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25350             
25351             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25352         }
25353         
25354         this.el.select('>button.close').on('click', this.hide, this);
25355         
25356     },
25357     
25358     show : function()
25359     {
25360         if (!this.rendered) {
25361             this.render();
25362         }
25363         
25364         this.el.show();
25365         
25366         this.fireEvent('show', this);
25367         
25368     },
25369     
25370     hide : function()
25371     {
25372         if (!this.rendered) {
25373             this.render();
25374         }
25375         
25376         this.el.hide();
25377         
25378         this.fireEvent('hide', this);
25379     },
25380     
25381     update : function()
25382     {
25383 //        var e = this.el.dom.firstChild;
25384 //        
25385 //        if(this.closable){
25386 //            e = e.nextSibling;
25387 //        }
25388 //        
25389 //        e.data = this.html || '';
25390
25391         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25392     }
25393    
25394 });
25395
25396  
25397
25398      /*
25399  * - LGPL
25400  *
25401  * Graph
25402  * 
25403  */
25404
25405
25406 /**
25407  * @class Roo.bootstrap.Graph
25408  * @extends Roo.bootstrap.Component
25409  * Bootstrap Graph class
25410 > Prameters
25411  -sm {number} sm 4
25412  -md {number} md 5
25413  @cfg {String} graphtype  bar | vbar | pie
25414  @cfg {number} g_x coodinator | centre x (pie)
25415  @cfg {number} g_y coodinator | centre y (pie)
25416  @cfg {number} g_r radius (pie)
25417  @cfg {number} g_height height of the chart (respected by all elements in the set)
25418  @cfg {number} g_width width of the chart (respected by all elements in the set)
25419  @cfg {Object} title The title of the chart
25420     
25421  -{Array}  values
25422  -opts (object) options for the chart 
25423      o {
25424      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25425      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25426      o vgutter (number)
25427      o colors (array) colors be used repeatedly to plot the bars. If multicolumn bar is used each sequence of bars with use a different color.
25428      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25429      o to
25430      o stretch (boolean)
25431      o }
25432  -opts (object) options for the pie
25433      o{
25434      o cut
25435      o startAngle (number)
25436      o endAngle (number)
25437      } 
25438  *
25439  * @constructor
25440  * Create a new Input
25441  * @param {Object} config The config object
25442  */
25443
25444 Roo.bootstrap.Graph = function(config){
25445     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25446     
25447     this.addEvents({
25448         // img events
25449         /**
25450          * @event click
25451          * The img click event for the img.
25452          * @param {Roo.EventObject} e
25453          */
25454         "click" : true
25455     });
25456 };
25457
25458 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25459     
25460     sm: 4,
25461     md: 5,
25462     graphtype: 'bar',
25463     g_height: 250,
25464     g_width: 400,
25465     g_x: 50,
25466     g_y: 50,
25467     g_r: 30,
25468     opts:{
25469         //g_colors: this.colors,
25470         g_type: 'soft',
25471         g_gutter: '20%'
25472
25473     },
25474     title : false,
25475
25476     getAutoCreate : function(){
25477         
25478         var cfg = {
25479             tag: 'div',
25480             html : null
25481         };
25482         
25483         
25484         return  cfg;
25485     },
25486
25487     onRender : function(ct,position){
25488         
25489         
25490         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25491         
25492         if (typeof(Raphael) == 'undefined') {
25493             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25494             return;
25495         }
25496         
25497         this.raphael = Raphael(this.el.dom);
25498         
25499                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25500                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25501                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25502                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25503                 /*
25504                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25505                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25506                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25507                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25508                 
25509                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25510                 r.barchart(330, 10, 300, 220, data1);
25511                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25512                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25513                 */
25514                 
25515                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25516                 // r.barchart(30, 30, 560, 250,  xdata, {
25517                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25518                 //     axis : "0 0 1 1",
25519                 //     axisxlabels :  xdata
25520                 //     //yvalues : cols,
25521                    
25522                 // });
25523 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25524 //        
25525 //        this.load(null,xdata,{
25526 //                axis : "0 0 1 1",
25527 //                axisxlabels :  xdata
25528 //                });
25529
25530     },
25531
25532     load : function(graphtype,xdata,opts)
25533     {
25534         this.raphael.clear();
25535         if(!graphtype) {
25536             graphtype = this.graphtype;
25537         }
25538         if(!opts){
25539             opts = this.opts;
25540         }
25541         var r = this.raphael,
25542             fin = function () {
25543                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25544             },
25545             fout = function () {
25546                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25547             },
25548             pfin = function() {
25549                 this.sector.stop();
25550                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25551
25552                 if (this.label) {
25553                     this.label[0].stop();
25554                     this.label[0].attr({ r: 7.5 });
25555                     this.label[1].attr({ "font-weight": 800 });
25556                 }
25557             },
25558             pfout = function() {
25559                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25560
25561                 if (this.label) {
25562                     this.label[0].animate({ r: 5 }, 500, "bounce");
25563                     this.label[1].attr({ "font-weight": 400 });
25564                 }
25565             };
25566
25567         switch(graphtype){
25568             case 'bar':
25569                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25570                 break;
25571             case 'hbar':
25572                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25573                 break;
25574             case 'pie':
25575 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25576 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25577 //            
25578                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25579                 
25580                 break;
25581
25582         }
25583         
25584         if(this.title){
25585             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25586         }
25587         
25588     },
25589     
25590     setTitle: function(o)
25591     {
25592         this.title = o;
25593     },
25594     
25595     initEvents: function() {
25596         
25597         if(!this.href){
25598             this.el.on('click', this.onClick, this);
25599         }
25600     },
25601     
25602     onClick : function(e)
25603     {
25604         Roo.log('img onclick');
25605         this.fireEvent('click', this, e);
25606     }
25607    
25608 });
25609
25610  
25611 /*
25612  * - LGPL
25613  *
25614  * numberBox
25615  * 
25616  */
25617 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25618
25619 /**
25620  * @class Roo.bootstrap.dash.NumberBox
25621  * @extends Roo.bootstrap.Component
25622  * Bootstrap NumberBox class
25623  * @cfg {String} headline Box headline
25624  * @cfg {String} content Box content
25625  * @cfg {String} icon Box icon
25626  * @cfg {String} footer Footer text
25627  * @cfg {String} fhref Footer href
25628  * 
25629  * @constructor
25630  * Create a new NumberBox
25631  * @param {Object} config The config object
25632  */
25633
25634
25635 Roo.bootstrap.dash.NumberBox = function(config){
25636     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25637     
25638 };
25639
25640 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25641     
25642     headline : '',
25643     content : '',
25644     icon : '',
25645     footer : '',
25646     fhref : '',
25647     ficon : '',
25648     
25649     getAutoCreate : function(){
25650         
25651         var cfg = {
25652             tag : 'div',
25653             cls : 'small-box ',
25654             cn : [
25655                 {
25656                     tag : 'div',
25657                     cls : 'inner',
25658                     cn :[
25659                         {
25660                             tag : 'h3',
25661                             cls : 'roo-headline',
25662                             html : this.headline
25663                         },
25664                         {
25665                             tag : 'p',
25666                             cls : 'roo-content',
25667                             html : this.content
25668                         }
25669                     ]
25670                 }
25671             ]
25672         };
25673         
25674         if(this.icon){
25675             cfg.cn.push({
25676                 tag : 'div',
25677                 cls : 'icon',
25678                 cn :[
25679                     {
25680                         tag : 'i',
25681                         cls : 'ion ' + this.icon
25682                     }
25683                 ]
25684             });
25685         }
25686         
25687         if(this.footer){
25688             var footer = {
25689                 tag : 'a',
25690                 cls : 'small-box-footer',
25691                 href : this.fhref || '#',
25692                 html : this.footer
25693             };
25694             
25695             cfg.cn.push(footer);
25696             
25697         }
25698         
25699         return  cfg;
25700     },
25701
25702     onRender : function(ct,position){
25703         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25704
25705
25706        
25707                 
25708     },
25709
25710     setHeadline: function (value)
25711     {
25712         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25713     },
25714     
25715     setFooter: function (value, href)
25716     {
25717         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25718         
25719         if(href){
25720             this.el.select('a.small-box-footer',true).first().attr('href', href);
25721         }
25722         
25723     },
25724
25725     setContent: function (value)
25726     {
25727         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25728     },
25729
25730     initEvents: function() 
25731     {   
25732         
25733     }
25734     
25735 });
25736
25737  
25738 /*
25739  * - LGPL
25740  *
25741  * TabBox
25742  * 
25743  */
25744 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25745
25746 /**
25747  * @class Roo.bootstrap.dash.TabBox
25748  * @extends Roo.bootstrap.Component
25749  * Bootstrap TabBox class
25750  * @cfg {String} title Title of the TabBox
25751  * @cfg {String} icon Icon of the TabBox
25752  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25753  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25754  * 
25755  * @constructor
25756  * Create a new TabBox
25757  * @param {Object} config The config object
25758  */
25759
25760
25761 Roo.bootstrap.dash.TabBox = function(config){
25762     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25763     this.addEvents({
25764         // raw events
25765         /**
25766          * @event addpane
25767          * When a pane is added
25768          * @param {Roo.bootstrap.dash.TabPane} pane
25769          */
25770         "addpane" : true,
25771         /**
25772          * @event activatepane
25773          * When a pane is activated
25774          * @param {Roo.bootstrap.dash.TabPane} pane
25775          */
25776         "activatepane" : true
25777         
25778          
25779     });
25780     
25781     this.panes = [];
25782 };
25783
25784 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25785
25786     title : '',
25787     icon : false,
25788     showtabs : true,
25789     tabScrollable : false,
25790     
25791     getChildContainer : function()
25792     {
25793         return this.el.select('.tab-content', true).first();
25794     },
25795     
25796     getAutoCreate : function(){
25797         
25798         var header = {
25799             tag: 'li',
25800             cls: 'pull-left header',
25801             html: this.title,
25802             cn : []
25803         };
25804         
25805         if(this.icon){
25806             header.cn.push({
25807                 tag: 'i',
25808                 cls: 'fa ' + this.icon
25809             });
25810         }
25811         
25812         var h = {
25813             tag: 'ul',
25814             cls: 'nav nav-tabs pull-right',
25815             cn: [
25816                 header
25817             ]
25818         };
25819         
25820         if(this.tabScrollable){
25821             h = {
25822                 tag: 'div',
25823                 cls: 'tab-header',
25824                 cn: [
25825                     {
25826                         tag: 'ul',
25827                         cls: 'nav nav-tabs pull-right',
25828                         cn: [
25829                             header
25830                         ]
25831                     }
25832                 ]
25833             };
25834         }
25835         
25836         var cfg = {
25837             tag: 'div',
25838             cls: 'nav-tabs-custom',
25839             cn: [
25840                 h,
25841                 {
25842                     tag: 'div',
25843                     cls: 'tab-content no-padding',
25844                     cn: []
25845                 }
25846             ]
25847         };
25848
25849         return  cfg;
25850     },
25851     initEvents : function()
25852     {
25853         //Roo.log('add add pane handler');
25854         this.on('addpane', this.onAddPane, this);
25855     },
25856      /**
25857      * Updates the box title
25858      * @param {String} html to set the title to.
25859      */
25860     setTitle : function(value)
25861     {
25862         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25863     },
25864     onAddPane : function(pane)
25865     {
25866         this.panes.push(pane);
25867         //Roo.log('addpane');
25868         //Roo.log(pane);
25869         // tabs are rendere left to right..
25870         if(!this.showtabs){
25871             return;
25872         }
25873         
25874         var ctr = this.el.select('.nav-tabs', true).first();
25875          
25876          
25877         var existing = ctr.select('.nav-tab',true);
25878         var qty = existing.getCount();;
25879         
25880         
25881         var tab = ctr.createChild({
25882             tag : 'li',
25883             cls : 'nav-tab' + (qty ? '' : ' active'),
25884             cn : [
25885                 {
25886                     tag : 'a',
25887                     href:'#',
25888                     html : pane.title
25889                 }
25890             ]
25891         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25892         pane.tab = tab;
25893         
25894         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25895         if (!qty) {
25896             pane.el.addClass('active');
25897         }
25898         
25899                 
25900     },
25901     onTabClick : function(ev,un,ob,pane)
25902     {
25903         //Roo.log('tab - prev default');
25904         ev.preventDefault();
25905         
25906         
25907         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25908         pane.tab.addClass('active');
25909         //Roo.log(pane.title);
25910         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25911         // technically we should have a deactivate event.. but maybe add later.
25912         // and it should not de-activate the selected tab...
25913         this.fireEvent('activatepane', pane);
25914         pane.el.addClass('active');
25915         pane.fireEvent('activate');
25916         
25917         
25918     },
25919     
25920     getActivePane : function()
25921     {
25922         var r = false;
25923         Roo.each(this.panes, function(p) {
25924             if(p.el.hasClass('active')){
25925                 r = p;
25926                 return false;
25927             }
25928             
25929             return;
25930         });
25931         
25932         return r;
25933     }
25934     
25935     
25936 });
25937
25938  
25939 /*
25940  * - LGPL
25941  *
25942  * Tab pane
25943  * 
25944  */
25945 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25946 /**
25947  * @class Roo.bootstrap.TabPane
25948  * @extends Roo.bootstrap.Component
25949  * Bootstrap TabPane class
25950  * @cfg {Boolean} active (false | true) Default false
25951  * @cfg {String} title title of panel
25952
25953  * 
25954  * @constructor
25955  * Create a new TabPane
25956  * @param {Object} config The config object
25957  */
25958
25959 Roo.bootstrap.dash.TabPane = function(config){
25960     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25961     
25962     this.addEvents({
25963         // raw events
25964         /**
25965          * @event activate
25966          * When a pane is activated
25967          * @param {Roo.bootstrap.dash.TabPane} pane
25968          */
25969         "activate" : true
25970          
25971     });
25972 };
25973
25974 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25975     
25976     active : false,
25977     title : '',
25978     
25979     // the tabBox that this is attached to.
25980     tab : false,
25981      
25982     getAutoCreate : function() 
25983     {
25984         var cfg = {
25985             tag: 'div',
25986             cls: 'tab-pane'
25987         };
25988         
25989         if(this.active){
25990             cfg.cls += ' active';
25991         }
25992         
25993         return cfg;
25994     },
25995     initEvents  : function()
25996     {
25997         //Roo.log('trigger add pane handler');
25998         this.parent().fireEvent('addpane', this)
25999     },
26000     
26001      /**
26002      * Updates the tab title 
26003      * @param {String} html to set the title to.
26004      */
26005     setTitle: function(str)
26006     {
26007         if (!this.tab) {
26008             return;
26009         }
26010         this.title = str;
26011         this.tab.select('a', true).first().dom.innerHTML = str;
26012         
26013     }
26014     
26015     
26016     
26017 });
26018
26019  
26020
26021
26022  /*
26023  * - LGPL
26024  *
26025  * menu
26026  * 
26027  */
26028 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26029
26030 /**
26031  * @class Roo.bootstrap.menu.Menu
26032  * @extends Roo.bootstrap.Component
26033  * Bootstrap Menu class - container for Menu
26034  * @cfg {String} html Text of the menu
26035  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
26036  * @cfg {String} icon Font awesome icon
26037  * @cfg {String} pos Menu align to (top | bottom) default bottom
26038  * 
26039  * 
26040  * @constructor
26041  * Create a new Menu
26042  * @param {Object} config The config object
26043  */
26044
26045
26046 Roo.bootstrap.menu.Menu = function(config){
26047     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26048     
26049     this.addEvents({
26050         /**
26051          * @event beforeshow
26052          * Fires before this menu is displayed
26053          * @param {Roo.bootstrap.menu.Menu} this
26054          */
26055         beforeshow : true,
26056         /**
26057          * @event beforehide
26058          * Fires before this menu is hidden
26059          * @param {Roo.bootstrap.menu.Menu} this
26060          */
26061         beforehide : true,
26062         /**
26063          * @event show
26064          * Fires after this menu is displayed
26065          * @param {Roo.bootstrap.menu.Menu} this
26066          */
26067         show : true,
26068         /**
26069          * @event hide
26070          * Fires after this menu is hidden
26071          * @param {Roo.bootstrap.menu.Menu} this
26072          */
26073         hide : true,
26074         /**
26075          * @event click
26076          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26077          * @param {Roo.bootstrap.menu.Menu} this
26078          * @param {Roo.EventObject} e
26079          */
26080         click : true
26081     });
26082     
26083 };
26084
26085 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26086     
26087     submenu : false,
26088     html : '',
26089     weight : 'default',
26090     icon : false,
26091     pos : 'bottom',
26092     
26093     
26094     getChildContainer : function() {
26095         if(this.isSubMenu){
26096             return this.el;
26097         }
26098         
26099         return this.el.select('ul.dropdown-menu', true).first();  
26100     },
26101     
26102     getAutoCreate : function()
26103     {
26104         var text = [
26105             {
26106                 tag : 'span',
26107                 cls : 'roo-menu-text',
26108                 html : this.html
26109             }
26110         ];
26111         
26112         if(this.icon){
26113             text.unshift({
26114                 tag : 'i',
26115                 cls : 'fa ' + this.icon
26116             })
26117         }
26118         
26119         
26120         var cfg = {
26121             tag : 'div',
26122             cls : 'btn-group',
26123             cn : [
26124                 {
26125                     tag : 'button',
26126                     cls : 'dropdown-button btn btn-' + this.weight,
26127                     cn : text
26128                 },
26129                 {
26130                     tag : 'button',
26131                     cls : 'dropdown-toggle btn btn-' + this.weight,
26132                     cn : [
26133                         {
26134                             tag : 'span',
26135                             cls : 'caret'
26136                         }
26137                     ]
26138                 },
26139                 {
26140                     tag : 'ul',
26141                     cls : 'dropdown-menu'
26142                 }
26143             ]
26144             
26145         };
26146         
26147         if(this.pos == 'top'){
26148             cfg.cls += ' dropup';
26149         }
26150         
26151         if(this.isSubMenu){
26152             cfg = {
26153                 tag : 'ul',
26154                 cls : 'dropdown-menu'
26155             }
26156         }
26157         
26158         return cfg;
26159     },
26160     
26161     onRender : function(ct, position)
26162     {
26163         this.isSubMenu = ct.hasClass('dropdown-submenu');
26164         
26165         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26166     },
26167     
26168     initEvents : function() 
26169     {
26170         if(this.isSubMenu){
26171             return;
26172         }
26173         
26174         this.hidden = true;
26175         
26176         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26177         this.triggerEl.on('click', this.onTriggerPress, this);
26178         
26179         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26180         this.buttonEl.on('click', this.onClick, this);
26181         
26182     },
26183     
26184     list : function()
26185     {
26186         if(this.isSubMenu){
26187             return this.el;
26188         }
26189         
26190         return this.el.select('ul.dropdown-menu', true).first();
26191     },
26192     
26193     onClick : function(e)
26194     {
26195         this.fireEvent("click", this, e);
26196     },
26197     
26198     onTriggerPress  : function(e)
26199     {   
26200         if (this.isVisible()) {
26201             this.hide();
26202         } else {
26203             this.show();
26204         }
26205     },
26206     
26207     isVisible : function(){
26208         return !this.hidden;
26209     },
26210     
26211     show : function()
26212     {
26213         this.fireEvent("beforeshow", this);
26214         
26215         this.hidden = false;
26216         this.el.addClass('open');
26217         
26218         Roo.get(document).on("mouseup", this.onMouseUp, this);
26219         
26220         this.fireEvent("show", this);
26221         
26222         
26223     },
26224     
26225     hide : function()
26226     {
26227         this.fireEvent("beforehide", this);
26228         
26229         this.hidden = true;
26230         this.el.removeClass('open');
26231         
26232         Roo.get(document).un("mouseup", this.onMouseUp);
26233         
26234         this.fireEvent("hide", this);
26235     },
26236     
26237     onMouseUp : function()
26238     {
26239         this.hide();
26240     }
26241     
26242 });
26243
26244  
26245  /*
26246  * - LGPL
26247  *
26248  * menu item
26249  * 
26250  */
26251 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26252
26253 /**
26254  * @class Roo.bootstrap.menu.Item
26255  * @extends Roo.bootstrap.Component
26256  * Bootstrap MenuItem class
26257  * @cfg {Boolean} submenu (true | false) default false
26258  * @cfg {String} html text of the item
26259  * @cfg {String} href the link
26260  * @cfg {Boolean} disable (true | false) default false
26261  * @cfg {Boolean} preventDefault (true | false) default true
26262  * @cfg {String} icon Font awesome icon
26263  * @cfg {String} pos Submenu align to (left | right) default right 
26264  * 
26265  * 
26266  * @constructor
26267  * Create a new Item
26268  * @param {Object} config The config object
26269  */
26270
26271
26272 Roo.bootstrap.menu.Item = function(config){
26273     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26274     this.addEvents({
26275         /**
26276          * @event mouseover
26277          * Fires when the mouse is hovering over this menu
26278          * @param {Roo.bootstrap.menu.Item} this
26279          * @param {Roo.EventObject} e
26280          */
26281         mouseover : true,
26282         /**
26283          * @event mouseout
26284          * Fires when the mouse exits this menu
26285          * @param {Roo.bootstrap.menu.Item} this
26286          * @param {Roo.EventObject} e
26287          */
26288         mouseout : true,
26289         // raw events
26290         /**
26291          * @event click
26292          * The raw click event for the entire grid.
26293          * @param {Roo.EventObject} e
26294          */
26295         click : true
26296     });
26297 };
26298
26299 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26300     
26301     submenu : false,
26302     href : '',
26303     html : '',
26304     preventDefault: true,
26305     disable : false,
26306     icon : false,
26307     pos : 'right',
26308     
26309     getAutoCreate : function()
26310     {
26311         var text = [
26312             {
26313                 tag : 'span',
26314                 cls : 'roo-menu-item-text',
26315                 html : this.html
26316             }
26317         ];
26318         
26319         if(this.icon){
26320             text.unshift({
26321                 tag : 'i',
26322                 cls : 'fa ' + this.icon
26323             })
26324         }
26325         
26326         var cfg = {
26327             tag : 'li',
26328             cn : [
26329                 {
26330                     tag : 'a',
26331                     href : this.href || '#',
26332                     cn : text
26333                 }
26334             ]
26335         };
26336         
26337         if(this.disable){
26338             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26339         }
26340         
26341         if(this.submenu){
26342             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26343             
26344             if(this.pos == 'left'){
26345                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26346             }
26347         }
26348         
26349         return cfg;
26350     },
26351     
26352     initEvents : function() 
26353     {
26354         this.el.on('mouseover', this.onMouseOver, this);
26355         this.el.on('mouseout', this.onMouseOut, this);
26356         
26357         this.el.select('a', true).first().on('click', this.onClick, this);
26358         
26359     },
26360     
26361     onClick : function(e)
26362     {
26363         if(this.preventDefault){
26364             e.preventDefault();
26365         }
26366         
26367         this.fireEvent("click", this, e);
26368     },
26369     
26370     onMouseOver : function(e)
26371     {
26372         if(this.submenu && this.pos == 'left'){
26373             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26374         }
26375         
26376         this.fireEvent("mouseover", this, e);
26377     },
26378     
26379     onMouseOut : function(e)
26380     {
26381         this.fireEvent("mouseout", this, e);
26382     }
26383 });
26384
26385  
26386
26387  /*
26388  * - LGPL
26389  *
26390  * menu separator
26391  * 
26392  */
26393 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26394
26395 /**
26396  * @class Roo.bootstrap.menu.Separator
26397  * @extends Roo.bootstrap.Component
26398  * Bootstrap Separator class
26399  * 
26400  * @constructor
26401  * Create a new Separator
26402  * @param {Object} config The config object
26403  */
26404
26405
26406 Roo.bootstrap.menu.Separator = function(config){
26407     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26408 };
26409
26410 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26411     
26412     getAutoCreate : function(){
26413         var cfg = {
26414             tag : 'li',
26415             cls: 'divider'
26416         };
26417         
26418         return cfg;
26419     }
26420    
26421 });
26422
26423  
26424
26425  /*
26426  * - LGPL
26427  *
26428  * Tooltip
26429  * 
26430  */
26431
26432 /**
26433  * @class Roo.bootstrap.Tooltip
26434  * Bootstrap Tooltip class
26435  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26436  * to determine which dom element triggers the tooltip.
26437  * 
26438  * It needs to add support for additional attributes like tooltip-position
26439  * 
26440  * @constructor
26441  * Create a new Toolti
26442  * @param {Object} config The config object
26443  */
26444
26445 Roo.bootstrap.Tooltip = function(config){
26446     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26447     
26448     this.alignment = Roo.bootstrap.Tooltip.alignment;
26449     
26450     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26451         this.alignment = config.alignment;
26452     }
26453     
26454 };
26455
26456 Roo.apply(Roo.bootstrap.Tooltip, {
26457     /**
26458      * @function init initialize tooltip monitoring.
26459      * @static
26460      */
26461     currentEl : false,
26462     currentTip : false,
26463     currentRegion : false,
26464     
26465     //  init : delay?
26466     
26467     init : function()
26468     {
26469         Roo.get(document).on('mouseover', this.enter ,this);
26470         Roo.get(document).on('mouseout', this.leave, this);
26471          
26472         
26473         this.currentTip = new Roo.bootstrap.Tooltip();
26474     },
26475     
26476     enter : function(ev)
26477     {
26478         var dom = ev.getTarget();
26479         
26480         //Roo.log(['enter',dom]);
26481         var el = Roo.fly(dom);
26482         if (this.currentEl) {
26483             //Roo.log(dom);
26484             //Roo.log(this.currentEl);
26485             //Roo.log(this.currentEl.contains(dom));
26486             if (this.currentEl == el) {
26487                 return;
26488             }
26489             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26490                 return;
26491             }
26492
26493         }
26494         
26495         if (this.currentTip.el) {
26496             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26497         }    
26498         //Roo.log(ev);
26499         
26500         if(!el || el.dom == document){
26501             return;
26502         }
26503         
26504         var bindEl = el;
26505         
26506         // you can not look for children, as if el is the body.. then everythign is the child..
26507         if (!el.attr('tooltip')) { //
26508             if (!el.select("[tooltip]").elements.length) {
26509                 return;
26510             }
26511             // is the mouse over this child...?
26512             bindEl = el.select("[tooltip]").first();
26513             var xy = ev.getXY();
26514             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26515                 //Roo.log("not in region.");
26516                 return;
26517             }
26518             //Roo.log("child element over..");
26519             
26520         }
26521         this.currentEl = bindEl;
26522         this.currentTip.bind(bindEl);
26523         this.currentRegion = Roo.lib.Region.getRegion(dom);
26524         this.currentTip.enter();
26525         
26526     },
26527     leave : function(ev)
26528     {
26529         var dom = ev.getTarget();
26530         //Roo.log(['leave',dom]);
26531         if (!this.currentEl) {
26532             return;
26533         }
26534         
26535         
26536         if (dom != this.currentEl.dom) {
26537             return;
26538         }
26539         var xy = ev.getXY();
26540         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26541             return;
26542         }
26543         // only activate leave if mouse cursor is outside... bounding box..
26544         
26545         
26546         
26547         
26548         if (this.currentTip) {
26549             this.currentTip.leave();
26550         }
26551         //Roo.log('clear currentEl');
26552         this.currentEl = false;
26553         
26554         
26555     },
26556     alignment : {
26557         'left' : ['r-l', [-2,0], 'right'],
26558         'right' : ['l-r', [2,0], 'left'],
26559         'bottom' : ['t-b', [0,2], 'top'],
26560         'top' : [ 'b-t', [0,-2], 'bottom']
26561     }
26562     
26563 });
26564
26565
26566 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26567     
26568     
26569     bindEl : false,
26570     
26571     delay : null, // can be { show : 300 , hide: 500}
26572     
26573     timeout : null,
26574     
26575     hoverState : null, //???
26576     
26577     placement : 'bottom', 
26578     
26579     alignment : false,
26580     
26581     getAutoCreate : function(){
26582     
26583         var cfg = {
26584            cls : 'tooltip',
26585            role : 'tooltip',
26586            cn : [
26587                 {
26588                     cls : 'tooltip-arrow'
26589                 },
26590                 {
26591                     cls : 'tooltip-inner'
26592                 }
26593            ]
26594         };
26595         
26596         return cfg;
26597     },
26598     bind : function(el)
26599     {
26600         this.bindEl = el;
26601     },
26602       
26603     
26604     enter : function () {
26605        
26606         if (this.timeout != null) {
26607             clearTimeout(this.timeout);
26608         }
26609         
26610         this.hoverState = 'in';
26611          //Roo.log("enter - show");
26612         if (!this.delay || !this.delay.show) {
26613             this.show();
26614             return;
26615         }
26616         var _t = this;
26617         this.timeout = setTimeout(function () {
26618             if (_t.hoverState == 'in') {
26619                 _t.show();
26620             }
26621         }, this.delay.show);
26622     },
26623     leave : function()
26624     {
26625         clearTimeout(this.timeout);
26626     
26627         this.hoverState = 'out';
26628          if (!this.delay || !this.delay.hide) {
26629             this.hide();
26630             return;
26631         }
26632        
26633         var _t = this;
26634         this.timeout = setTimeout(function () {
26635             //Roo.log("leave - timeout");
26636             
26637             if (_t.hoverState == 'out') {
26638                 _t.hide();
26639                 Roo.bootstrap.Tooltip.currentEl = false;
26640             }
26641         }, delay);
26642     },
26643     
26644     show : function (msg)
26645     {
26646         if (!this.el) {
26647             this.render(document.body);
26648         }
26649         // set content.
26650         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26651         
26652         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26653         
26654         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26655         
26656         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26657         
26658         var placement = typeof this.placement == 'function' ?
26659             this.placement.call(this, this.el, on_el) :
26660             this.placement;
26661             
26662         var autoToken = /\s?auto?\s?/i;
26663         var autoPlace = autoToken.test(placement);
26664         if (autoPlace) {
26665             placement = placement.replace(autoToken, '') || 'top';
26666         }
26667         
26668         //this.el.detach()
26669         //this.el.setXY([0,0]);
26670         this.el.show();
26671         //this.el.dom.style.display='block';
26672         
26673         //this.el.appendTo(on_el);
26674         
26675         var p = this.getPosition();
26676         var box = this.el.getBox();
26677         
26678         if (autoPlace) {
26679             // fixme..
26680         }
26681         
26682         var align = this.alignment[placement];
26683         
26684         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26685         
26686         if(placement == 'top' || placement == 'bottom'){
26687             if(xy[0] < 0){
26688                 placement = 'right';
26689             }
26690             
26691             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26692                 placement = 'left';
26693             }
26694             
26695             var scroll = Roo.select('body', true).first().getScroll();
26696             
26697             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26698                 placement = 'top';
26699             }
26700             
26701             align = this.alignment[placement];
26702         }
26703         
26704         this.el.alignTo(this.bindEl, align[0],align[1]);
26705         //var arrow = this.el.select('.arrow',true).first();
26706         //arrow.set(align[2], 
26707         
26708         this.el.addClass(placement);
26709         
26710         this.el.addClass('in fade');
26711         
26712         this.hoverState = null;
26713         
26714         if (this.el.hasClass('fade')) {
26715             // fade it?
26716         }
26717         
26718     },
26719     hide : function()
26720     {
26721          
26722         if (!this.el) {
26723             return;
26724         }
26725         //this.el.setXY([0,0]);
26726         this.el.removeClass('in');
26727         //this.el.hide();
26728         
26729     }
26730     
26731 });
26732  
26733
26734  /*
26735  * - LGPL
26736  *
26737  * Location Picker
26738  * 
26739  */
26740
26741 /**
26742  * @class Roo.bootstrap.LocationPicker
26743  * @extends Roo.bootstrap.Component
26744  * Bootstrap LocationPicker class
26745  * @cfg {Number} latitude Position when init default 0
26746  * @cfg {Number} longitude Position when init default 0
26747  * @cfg {Number} zoom default 15
26748  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26749  * @cfg {Boolean} mapTypeControl default false
26750  * @cfg {Boolean} disableDoubleClickZoom default false
26751  * @cfg {Boolean} scrollwheel default true
26752  * @cfg {Boolean} streetViewControl default false
26753  * @cfg {Number} radius default 0
26754  * @cfg {String} locationName
26755  * @cfg {Boolean} draggable default true
26756  * @cfg {Boolean} enableAutocomplete default false
26757  * @cfg {Boolean} enableReverseGeocode default true
26758  * @cfg {String} markerTitle
26759  * 
26760  * @constructor
26761  * Create a new LocationPicker
26762  * @param {Object} config The config object
26763  */
26764
26765
26766 Roo.bootstrap.LocationPicker = function(config){
26767     
26768     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26769     
26770     this.addEvents({
26771         /**
26772          * @event initial
26773          * Fires when the picker initialized.
26774          * @param {Roo.bootstrap.LocationPicker} this
26775          * @param {Google Location} location
26776          */
26777         initial : true,
26778         /**
26779          * @event positionchanged
26780          * Fires when the picker position changed.
26781          * @param {Roo.bootstrap.LocationPicker} this
26782          * @param {Google Location} location
26783          */
26784         positionchanged : true,
26785         /**
26786          * @event resize
26787          * Fires when the map resize.
26788          * @param {Roo.bootstrap.LocationPicker} this
26789          */
26790         resize : true,
26791         /**
26792          * @event show
26793          * Fires when the map show.
26794          * @param {Roo.bootstrap.LocationPicker} this
26795          */
26796         show : true,
26797         /**
26798          * @event hide
26799          * Fires when the map hide.
26800          * @param {Roo.bootstrap.LocationPicker} this
26801          */
26802         hide : true,
26803         /**
26804          * @event mapClick
26805          * Fires when click the map.
26806          * @param {Roo.bootstrap.LocationPicker} this
26807          * @param {Map event} e
26808          */
26809         mapClick : true,
26810         /**
26811          * @event mapRightClick
26812          * Fires when right click the map.
26813          * @param {Roo.bootstrap.LocationPicker} this
26814          * @param {Map event} e
26815          */
26816         mapRightClick : true,
26817         /**
26818          * @event markerClick
26819          * Fires when click the marker.
26820          * @param {Roo.bootstrap.LocationPicker} this
26821          * @param {Map event} e
26822          */
26823         markerClick : true,
26824         /**
26825          * @event markerRightClick
26826          * Fires when right click the marker.
26827          * @param {Roo.bootstrap.LocationPicker} this
26828          * @param {Map event} e
26829          */
26830         markerRightClick : true,
26831         /**
26832          * @event OverlayViewDraw
26833          * Fires when OverlayView Draw
26834          * @param {Roo.bootstrap.LocationPicker} this
26835          */
26836         OverlayViewDraw : true,
26837         /**
26838          * @event OverlayViewOnAdd
26839          * Fires when OverlayView Draw
26840          * @param {Roo.bootstrap.LocationPicker} this
26841          */
26842         OverlayViewOnAdd : true,
26843         /**
26844          * @event OverlayViewOnRemove
26845          * Fires when OverlayView Draw
26846          * @param {Roo.bootstrap.LocationPicker} this
26847          */
26848         OverlayViewOnRemove : true,
26849         /**
26850          * @event OverlayViewShow
26851          * Fires when OverlayView Draw
26852          * @param {Roo.bootstrap.LocationPicker} this
26853          * @param {Pixel} cpx
26854          */
26855         OverlayViewShow : true,
26856         /**
26857          * @event OverlayViewHide
26858          * Fires when OverlayView Draw
26859          * @param {Roo.bootstrap.LocationPicker} this
26860          */
26861         OverlayViewHide : true,
26862         /**
26863          * @event loadexception
26864          * Fires when load google lib failed.
26865          * @param {Roo.bootstrap.LocationPicker} this
26866          */
26867         loadexception : true
26868     });
26869         
26870 };
26871
26872 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26873     
26874     gMapContext: false,
26875     
26876     latitude: 0,
26877     longitude: 0,
26878     zoom: 15,
26879     mapTypeId: false,
26880     mapTypeControl: false,
26881     disableDoubleClickZoom: false,
26882     scrollwheel: true,
26883     streetViewControl: false,
26884     radius: 0,
26885     locationName: '',
26886     draggable: true,
26887     enableAutocomplete: false,
26888     enableReverseGeocode: true,
26889     markerTitle: '',
26890     
26891     getAutoCreate: function()
26892     {
26893
26894         var cfg = {
26895             tag: 'div',
26896             cls: 'roo-location-picker'
26897         };
26898         
26899         return cfg
26900     },
26901     
26902     initEvents: function(ct, position)
26903     {       
26904         if(!this.el.getWidth() || this.isApplied()){
26905             return;
26906         }
26907         
26908         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26909         
26910         this.initial();
26911     },
26912     
26913     initial: function()
26914     {
26915         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26916             this.fireEvent('loadexception', this);
26917             return;
26918         }
26919         
26920         if(!this.mapTypeId){
26921             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26922         }
26923         
26924         this.gMapContext = this.GMapContext();
26925         
26926         this.initOverlayView();
26927         
26928         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26929         
26930         var _this = this;
26931                 
26932         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26933             _this.setPosition(_this.gMapContext.marker.position);
26934         });
26935         
26936         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26937             _this.fireEvent('mapClick', this, event);
26938             
26939         });
26940
26941         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26942             _this.fireEvent('mapRightClick', this, event);
26943             
26944         });
26945         
26946         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26947             _this.fireEvent('markerClick', this, event);
26948             
26949         });
26950
26951         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26952             _this.fireEvent('markerRightClick', this, event);
26953             
26954         });
26955         
26956         this.setPosition(this.gMapContext.location);
26957         
26958         this.fireEvent('initial', this, this.gMapContext.location);
26959     },
26960     
26961     initOverlayView: function()
26962     {
26963         var _this = this;
26964         
26965         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26966             
26967             draw: function()
26968             {
26969                 _this.fireEvent('OverlayViewDraw', _this);
26970             },
26971             
26972             onAdd: function()
26973             {
26974                 _this.fireEvent('OverlayViewOnAdd', _this);
26975             },
26976             
26977             onRemove: function()
26978             {
26979                 _this.fireEvent('OverlayViewOnRemove', _this);
26980             },
26981             
26982             show: function(cpx)
26983             {
26984                 _this.fireEvent('OverlayViewShow', _this, cpx);
26985             },
26986             
26987             hide: function()
26988             {
26989                 _this.fireEvent('OverlayViewHide', _this);
26990             }
26991             
26992         });
26993     },
26994     
26995     fromLatLngToContainerPixel: function(event)
26996     {
26997         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26998     },
26999     
27000     isApplied: function() 
27001     {
27002         return this.getGmapContext() == false ? false : true;
27003     },
27004     
27005     getGmapContext: function() 
27006     {
27007         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
27008     },
27009     
27010     GMapContext: function() 
27011     {
27012         var position = new google.maps.LatLng(this.latitude, this.longitude);
27013         
27014         var _map = new google.maps.Map(this.el.dom, {
27015             center: position,
27016             zoom: this.zoom,
27017             mapTypeId: this.mapTypeId,
27018             mapTypeControl: this.mapTypeControl,
27019             disableDoubleClickZoom: this.disableDoubleClickZoom,
27020             scrollwheel: this.scrollwheel,
27021             streetViewControl: this.streetViewControl,
27022             locationName: this.locationName,
27023             draggable: this.draggable,
27024             enableAutocomplete: this.enableAutocomplete,
27025             enableReverseGeocode: this.enableReverseGeocode
27026         });
27027         
27028         var _marker = new google.maps.Marker({
27029             position: position,
27030             map: _map,
27031             title: this.markerTitle,
27032             draggable: this.draggable
27033         });
27034         
27035         return {
27036             map: _map,
27037             marker: _marker,
27038             circle: null,
27039             location: position,
27040             radius: this.radius,
27041             locationName: this.locationName,
27042             addressComponents: {
27043                 formatted_address: null,
27044                 addressLine1: null,
27045                 addressLine2: null,
27046                 streetName: null,
27047                 streetNumber: null,
27048                 city: null,
27049                 district: null,
27050                 state: null,
27051                 stateOrProvince: null
27052             },
27053             settings: this,
27054             domContainer: this.el.dom,
27055             geodecoder: new google.maps.Geocoder()
27056         };
27057     },
27058     
27059     drawCircle: function(center, radius, options) 
27060     {
27061         if (this.gMapContext.circle != null) {
27062             this.gMapContext.circle.setMap(null);
27063         }
27064         if (radius > 0) {
27065             radius *= 1;
27066             options = Roo.apply({}, options, {
27067                 strokeColor: "#0000FF",
27068                 strokeOpacity: .35,
27069                 strokeWeight: 2,
27070                 fillColor: "#0000FF",
27071                 fillOpacity: .2
27072             });
27073             
27074             options.map = this.gMapContext.map;
27075             options.radius = radius;
27076             options.center = center;
27077             this.gMapContext.circle = new google.maps.Circle(options);
27078             return this.gMapContext.circle;
27079         }
27080         
27081         return null;
27082     },
27083     
27084     setPosition: function(location) 
27085     {
27086         this.gMapContext.location = location;
27087         this.gMapContext.marker.setPosition(location);
27088         this.gMapContext.map.panTo(location);
27089         this.drawCircle(location, this.gMapContext.radius, {});
27090         
27091         var _this = this;
27092         
27093         if (this.gMapContext.settings.enableReverseGeocode) {
27094             this.gMapContext.geodecoder.geocode({
27095                 latLng: this.gMapContext.location
27096             }, function(results, status) {
27097                 
27098                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27099                     _this.gMapContext.locationName = results[0].formatted_address;
27100                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27101                     
27102                     _this.fireEvent('positionchanged', this, location);
27103                 }
27104             });
27105             
27106             return;
27107         }
27108         
27109         this.fireEvent('positionchanged', this, location);
27110     },
27111     
27112     resize: function()
27113     {
27114         google.maps.event.trigger(this.gMapContext.map, "resize");
27115         
27116         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27117         
27118         this.fireEvent('resize', this);
27119     },
27120     
27121     setPositionByLatLng: function(latitude, longitude)
27122     {
27123         this.setPosition(new google.maps.LatLng(latitude, longitude));
27124     },
27125     
27126     getCurrentPosition: function() 
27127     {
27128         return {
27129             latitude: this.gMapContext.location.lat(),
27130             longitude: this.gMapContext.location.lng()
27131         };
27132     },
27133     
27134     getAddressName: function() 
27135     {
27136         return this.gMapContext.locationName;
27137     },
27138     
27139     getAddressComponents: function() 
27140     {
27141         return this.gMapContext.addressComponents;
27142     },
27143     
27144     address_component_from_google_geocode: function(address_components) 
27145     {
27146         var result = {};
27147         
27148         for (var i = 0; i < address_components.length; i++) {
27149             var component = address_components[i];
27150             if (component.types.indexOf("postal_code") >= 0) {
27151                 result.postalCode = component.short_name;
27152             } else if (component.types.indexOf("street_number") >= 0) {
27153                 result.streetNumber = component.short_name;
27154             } else if (component.types.indexOf("route") >= 0) {
27155                 result.streetName = component.short_name;
27156             } else if (component.types.indexOf("neighborhood") >= 0) {
27157                 result.city = component.short_name;
27158             } else if (component.types.indexOf("locality") >= 0) {
27159                 result.city = component.short_name;
27160             } else if (component.types.indexOf("sublocality") >= 0) {
27161                 result.district = component.short_name;
27162             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27163                 result.stateOrProvince = component.short_name;
27164             } else if (component.types.indexOf("country") >= 0) {
27165                 result.country = component.short_name;
27166             }
27167         }
27168         
27169         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27170         result.addressLine2 = "";
27171         return result;
27172     },
27173     
27174     setZoomLevel: function(zoom)
27175     {
27176         this.gMapContext.map.setZoom(zoom);
27177     },
27178     
27179     show: function()
27180     {
27181         if(!this.el){
27182             return;
27183         }
27184         
27185         this.el.show();
27186         
27187         this.resize();
27188         
27189         this.fireEvent('show', this);
27190     },
27191     
27192     hide: function()
27193     {
27194         if(!this.el){
27195             return;
27196         }
27197         
27198         this.el.hide();
27199         
27200         this.fireEvent('hide', this);
27201     }
27202     
27203 });
27204
27205 Roo.apply(Roo.bootstrap.LocationPicker, {
27206     
27207     OverlayView : function(map, options)
27208     {
27209         options = options || {};
27210         
27211         this.setMap(map);
27212     }
27213     
27214     
27215 });/*
27216  * - LGPL
27217  *
27218  * Alert
27219  * 
27220  */
27221
27222 /**
27223  * @class Roo.bootstrap.Alert
27224  * @extends Roo.bootstrap.Component
27225  * Bootstrap Alert class
27226  * @cfg {String} title The title of alert
27227  * @cfg {String} html The content of alert
27228  * @cfg {String} weight (  success | info | warning | danger )
27229  * @cfg {String} faicon font-awesomeicon
27230  * 
27231  * @constructor
27232  * Create a new alert
27233  * @param {Object} config The config object
27234  */
27235
27236
27237 Roo.bootstrap.Alert = function(config){
27238     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27239     
27240 };
27241
27242 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27243     
27244     title: '',
27245     html: '',
27246     weight: false,
27247     faicon: false,
27248     
27249     getAutoCreate : function()
27250     {
27251         
27252         var cfg = {
27253             tag : 'div',
27254             cls : 'alert',
27255             cn : [
27256                 {
27257                     tag : 'i',
27258                     cls : 'roo-alert-icon'
27259                     
27260                 },
27261                 {
27262                     tag : 'b',
27263                     cls : 'roo-alert-title',
27264                     html : this.title
27265                 },
27266                 {
27267                     tag : 'span',
27268                     cls : 'roo-alert-text',
27269                     html : this.html
27270                 }
27271             ]
27272         };
27273         
27274         if(this.faicon){
27275             cfg.cn[0].cls += ' fa ' + this.faicon;
27276         }
27277         
27278         if(this.weight){
27279             cfg.cls += ' alert-' + this.weight;
27280         }
27281         
27282         return cfg;
27283     },
27284     
27285     initEvents: function() 
27286     {
27287         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27288     },
27289     
27290     setTitle : function(str)
27291     {
27292         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27293     },
27294     
27295     setText : function(str)
27296     {
27297         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27298     },
27299     
27300     setWeight : function(weight)
27301     {
27302         if(this.weight){
27303             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27304         }
27305         
27306         this.weight = weight;
27307         
27308         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27309     },
27310     
27311     setIcon : function(icon)
27312     {
27313         if(this.faicon){
27314             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27315         }
27316         
27317         this.faicon = icon;
27318         
27319         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27320     },
27321     
27322     hide: function() 
27323     {
27324         this.el.hide();   
27325     },
27326     
27327     show: function() 
27328     {  
27329         this.el.show();   
27330     }
27331     
27332 });
27333
27334  
27335 /*
27336 * Licence: LGPL
27337 */
27338
27339 /**
27340  * @class Roo.bootstrap.UploadCropbox
27341  * @extends Roo.bootstrap.Component
27342  * Bootstrap UploadCropbox class
27343  * @cfg {String} emptyText show when image has been loaded
27344  * @cfg {String} rotateNotify show when image too small to rotate
27345  * @cfg {Number} errorTimeout default 3000
27346  * @cfg {Number} minWidth default 300
27347  * @cfg {Number} minHeight default 300
27348  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27349  * @cfg {Boolean} isDocument (true|false) default false
27350  * @cfg {String} url action url
27351  * @cfg {String} paramName default 'imageUpload'
27352  * @cfg {String} method default POST
27353  * @cfg {Boolean} loadMask (true|false) default true
27354  * @cfg {Boolean} loadingText default 'Loading...'
27355  * 
27356  * @constructor
27357  * Create a new UploadCropbox
27358  * @param {Object} config The config object
27359  */
27360
27361 Roo.bootstrap.UploadCropbox = function(config){
27362     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27363     
27364     this.addEvents({
27365         /**
27366          * @event beforeselectfile
27367          * Fire before select file
27368          * @param {Roo.bootstrap.UploadCropbox} this
27369          */
27370         "beforeselectfile" : true,
27371         /**
27372          * @event initial
27373          * Fire after initEvent
27374          * @param {Roo.bootstrap.UploadCropbox} this
27375          */
27376         "initial" : true,
27377         /**
27378          * @event crop
27379          * Fire after initEvent
27380          * @param {Roo.bootstrap.UploadCropbox} this
27381          * @param {String} data
27382          */
27383         "crop" : true,
27384         /**
27385          * @event prepare
27386          * Fire when preparing the file data
27387          * @param {Roo.bootstrap.UploadCropbox} this
27388          * @param {Object} file
27389          */
27390         "prepare" : true,
27391         /**
27392          * @event exception
27393          * Fire when get exception
27394          * @param {Roo.bootstrap.UploadCropbox} this
27395          * @param {XMLHttpRequest} xhr
27396          */
27397         "exception" : true,
27398         /**
27399          * @event beforeloadcanvas
27400          * Fire before load the canvas
27401          * @param {Roo.bootstrap.UploadCropbox} this
27402          * @param {String} src
27403          */
27404         "beforeloadcanvas" : true,
27405         /**
27406          * @event trash
27407          * Fire when trash image
27408          * @param {Roo.bootstrap.UploadCropbox} this
27409          */
27410         "trash" : true,
27411         /**
27412          * @event download
27413          * Fire when download the image
27414          * @param {Roo.bootstrap.UploadCropbox} this
27415          */
27416         "download" : true,
27417         /**
27418          * @event footerbuttonclick
27419          * Fire when footerbuttonclick
27420          * @param {Roo.bootstrap.UploadCropbox} this
27421          * @param {String} type
27422          */
27423         "footerbuttonclick" : true,
27424         /**
27425          * @event resize
27426          * Fire when resize
27427          * @param {Roo.bootstrap.UploadCropbox} this
27428          */
27429         "resize" : true,
27430         /**
27431          * @event rotate
27432          * Fire when rotate the image
27433          * @param {Roo.bootstrap.UploadCropbox} this
27434          * @param {String} pos
27435          */
27436         "rotate" : true,
27437         /**
27438          * @event inspect
27439          * Fire when inspect the file
27440          * @param {Roo.bootstrap.UploadCropbox} this
27441          * @param {Object} file
27442          */
27443         "inspect" : true,
27444         /**
27445          * @event upload
27446          * Fire when xhr upload the file
27447          * @param {Roo.bootstrap.UploadCropbox} this
27448          * @param {Object} data
27449          */
27450         "upload" : true,
27451         /**
27452          * @event arrange
27453          * Fire when arrange the file data
27454          * @param {Roo.bootstrap.UploadCropbox} this
27455          * @param {Object} formData
27456          */
27457         "arrange" : true
27458     });
27459     
27460     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27461 };
27462
27463 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27464     
27465     emptyText : 'Click to upload image',
27466     rotateNotify : 'Image is too small to rotate',
27467     errorTimeout : 3000,
27468     scale : 0,
27469     baseScale : 1,
27470     rotate : 0,
27471     dragable : false,
27472     pinching : false,
27473     mouseX : 0,
27474     mouseY : 0,
27475     cropData : false,
27476     minWidth : 300,
27477     minHeight : 300,
27478     file : false,
27479     exif : {},
27480     baseRotate : 1,
27481     cropType : 'image/jpeg',
27482     buttons : false,
27483     canvasLoaded : false,
27484     isDocument : false,
27485     method : 'POST',
27486     paramName : 'imageUpload',
27487     loadMask : true,
27488     loadingText : 'Loading...',
27489     maskEl : false,
27490     
27491     getAutoCreate : function()
27492     {
27493         var cfg = {
27494             tag : 'div',
27495             cls : 'roo-upload-cropbox',
27496             cn : [
27497                 {
27498                     tag : 'input',
27499                     cls : 'roo-upload-cropbox-selector',
27500                     type : 'file'
27501                 },
27502                 {
27503                     tag : 'div',
27504                     cls : 'roo-upload-cropbox-body',
27505                     style : 'cursor:pointer',
27506                     cn : [
27507                         {
27508                             tag : 'div',
27509                             cls : 'roo-upload-cropbox-preview'
27510                         },
27511                         {
27512                             tag : 'div',
27513                             cls : 'roo-upload-cropbox-thumb'
27514                         },
27515                         {
27516                             tag : 'div',
27517                             cls : 'roo-upload-cropbox-empty-notify',
27518                             html : this.emptyText
27519                         },
27520                         {
27521                             tag : 'div',
27522                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27523                             html : this.rotateNotify
27524                         }
27525                     ]
27526                 },
27527                 {
27528                     tag : 'div',
27529                     cls : 'roo-upload-cropbox-footer',
27530                     cn : {
27531                         tag : 'div',
27532                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27533                         cn : []
27534                     }
27535                 }
27536             ]
27537         };
27538         
27539         return cfg;
27540     },
27541     
27542     onRender : function(ct, position)
27543     {
27544         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27545         
27546         if (this.buttons.length) {
27547             
27548             Roo.each(this.buttons, function(bb) {
27549                 
27550                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27551                 
27552                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27553                 
27554             }, this);
27555         }
27556         
27557         if(this.loadMask){
27558             this.maskEl = this.el;
27559         }
27560     },
27561     
27562     initEvents : function()
27563     {
27564         this.urlAPI = (window.createObjectURL && window) || 
27565                                 (window.URL && URL.revokeObjectURL && URL) || 
27566                                 (window.webkitURL && webkitURL);
27567                         
27568         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27569         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27570         
27571         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27572         this.selectorEl.hide();
27573         
27574         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27575         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27576         
27577         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27578         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27579         this.thumbEl.hide();
27580         
27581         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27582         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27583         
27584         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27585         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27586         this.errorEl.hide();
27587         
27588         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27589         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27590         this.footerEl.hide();
27591         
27592         this.setThumbBoxSize();
27593         
27594         this.bind();
27595         
27596         this.resize();
27597         
27598         this.fireEvent('initial', this);
27599     },
27600
27601     bind : function()
27602     {
27603         var _this = this;
27604         
27605         window.addEventListener("resize", function() { _this.resize(); } );
27606         
27607         this.bodyEl.on('click', this.beforeSelectFile, this);
27608         
27609         if(Roo.isTouch){
27610             this.bodyEl.on('touchstart', this.onTouchStart, this);
27611             this.bodyEl.on('touchmove', this.onTouchMove, this);
27612             this.bodyEl.on('touchend', this.onTouchEnd, this);
27613         }
27614         
27615         if(!Roo.isTouch){
27616             this.bodyEl.on('mousedown', this.onMouseDown, this);
27617             this.bodyEl.on('mousemove', this.onMouseMove, this);
27618             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27619             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27620             Roo.get(document).on('mouseup', this.onMouseUp, this);
27621         }
27622         
27623         this.selectorEl.on('change', this.onFileSelected, this);
27624     },
27625     
27626     reset : function()
27627     {    
27628         this.scale = 0;
27629         this.baseScale = 1;
27630         this.rotate = 0;
27631         this.baseRotate = 1;
27632         this.dragable = false;
27633         this.pinching = false;
27634         this.mouseX = 0;
27635         this.mouseY = 0;
27636         this.cropData = false;
27637         this.notifyEl.dom.innerHTML = this.emptyText;
27638         
27639         this.selectorEl.dom.value = '';
27640         
27641     },
27642     
27643     resize : function()
27644     {
27645         if(this.fireEvent('resize', this) != false){
27646             this.setThumbBoxPosition();
27647             this.setCanvasPosition();
27648         }
27649     },
27650     
27651     onFooterButtonClick : function(e, el, o, type)
27652     {
27653         switch (type) {
27654             case 'rotate-left' :
27655                 this.onRotateLeft(e);
27656                 break;
27657             case 'rotate-right' :
27658                 this.onRotateRight(e);
27659                 break;
27660             case 'picture' :
27661                 this.beforeSelectFile(e);
27662                 break;
27663             case 'trash' :
27664                 this.trash(e);
27665                 break;
27666             case 'crop' :
27667                 this.crop(e);
27668                 break;
27669             case 'download' :
27670                 this.download(e);
27671                 break;
27672             default :
27673                 break;
27674         }
27675         
27676         this.fireEvent('footerbuttonclick', this, type);
27677     },
27678     
27679     beforeSelectFile : function(e)
27680     {
27681         e.preventDefault();
27682         
27683         if(this.fireEvent('beforeselectfile', this) != false){
27684             this.selectorEl.dom.click();
27685         }
27686     },
27687     
27688     onFileSelected : function(e)
27689     {
27690         e.preventDefault();
27691         
27692         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27693             return;
27694         }
27695         
27696         var file = this.selectorEl.dom.files[0];
27697         
27698         if(this.fireEvent('inspect', this, file) != false){
27699             this.prepare(file);
27700         }
27701         
27702     },
27703     
27704     trash : function(e)
27705     {
27706         this.fireEvent('trash', this);
27707     },
27708     
27709     download : function(e)
27710     {
27711         this.fireEvent('download', this);
27712     },
27713     
27714     loadCanvas : function(src)
27715     {   
27716         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27717             
27718             this.reset();
27719             
27720             this.imageEl = document.createElement('img');
27721             
27722             var _this = this;
27723             
27724             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27725             
27726             this.imageEl.src = src;
27727         }
27728     },
27729     
27730     onLoadCanvas : function()
27731     {   
27732         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27733         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27734         
27735         this.bodyEl.un('click', this.beforeSelectFile, this);
27736         
27737         this.notifyEl.hide();
27738         this.thumbEl.show();
27739         this.footerEl.show();
27740         
27741         this.baseRotateLevel();
27742         
27743         if(this.isDocument){
27744             this.setThumbBoxSize();
27745         }
27746         
27747         this.setThumbBoxPosition();
27748         
27749         this.baseScaleLevel();
27750         
27751         this.draw();
27752         
27753         this.resize();
27754         
27755         this.canvasLoaded = true;
27756         
27757         if(this.loadMask){
27758             this.maskEl.unmask();
27759         }
27760         
27761     },
27762     
27763     setCanvasPosition : function()
27764     {   
27765         if(!this.canvasEl){
27766             return;
27767         }
27768         
27769         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27770         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27771         
27772         this.previewEl.setLeft(pw);
27773         this.previewEl.setTop(ph);
27774         
27775     },
27776     
27777     onMouseDown : function(e)
27778     {   
27779         e.stopEvent();
27780         
27781         this.dragable = true;
27782         this.pinching = false;
27783         
27784         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27785             this.dragable = false;
27786             return;
27787         }
27788         
27789         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27790         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27791         
27792     },
27793     
27794     onMouseMove : function(e)
27795     {   
27796         e.stopEvent();
27797         
27798         if(!this.canvasLoaded){
27799             return;
27800         }
27801         
27802         if (!this.dragable){
27803             return;
27804         }
27805         
27806         var minX = Math.ceil(this.thumbEl.getLeft(true));
27807         var minY = Math.ceil(this.thumbEl.getTop(true));
27808         
27809         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27810         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27811         
27812         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27813         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27814         
27815         x = x - this.mouseX;
27816         y = y - this.mouseY;
27817         
27818         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27819         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27820         
27821         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27822         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27823         
27824         this.previewEl.setLeft(bgX);
27825         this.previewEl.setTop(bgY);
27826         
27827         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27828         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27829     },
27830     
27831     onMouseUp : function(e)
27832     {   
27833         e.stopEvent();
27834         
27835         this.dragable = false;
27836     },
27837     
27838     onMouseWheel : function(e)
27839     {   
27840         e.stopEvent();
27841         
27842         this.startScale = this.scale;
27843         
27844         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27845         
27846         if(!this.zoomable()){
27847             this.scale = this.startScale;
27848             return;
27849         }
27850         
27851         this.draw();
27852         
27853         return;
27854     },
27855     
27856     zoomable : function()
27857     {
27858         var minScale = this.thumbEl.getWidth() / this.minWidth;
27859         
27860         if(this.minWidth < this.minHeight){
27861             minScale = this.thumbEl.getHeight() / this.minHeight;
27862         }
27863         
27864         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27865         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27866         
27867         if(
27868                 this.isDocument &&
27869                 (this.rotate == 0 || this.rotate == 180) && 
27870                 (
27871                     width > this.imageEl.OriginWidth || 
27872                     height > this.imageEl.OriginHeight ||
27873                     (width < this.minWidth && height < this.minHeight)
27874                 )
27875         ){
27876             return false;
27877         }
27878         
27879         if(
27880                 this.isDocument &&
27881                 (this.rotate == 90 || this.rotate == 270) && 
27882                 (
27883                     width > this.imageEl.OriginWidth || 
27884                     height > this.imageEl.OriginHeight ||
27885                     (width < this.minHeight && height < this.minWidth)
27886                 )
27887         ){
27888             return false;
27889         }
27890         
27891         if(
27892                 !this.isDocument &&
27893                 (this.rotate == 0 || this.rotate == 180) && 
27894                 (
27895                     width < this.minWidth || 
27896                     width > this.imageEl.OriginWidth || 
27897                     height < this.minHeight || 
27898                     height > this.imageEl.OriginHeight
27899                 )
27900         ){
27901             return false;
27902         }
27903         
27904         if(
27905                 !this.isDocument &&
27906                 (this.rotate == 90 || this.rotate == 270) && 
27907                 (
27908                     width < this.minHeight || 
27909                     width > this.imageEl.OriginWidth || 
27910                     height < this.minWidth || 
27911                     height > this.imageEl.OriginHeight
27912                 )
27913         ){
27914             return false;
27915         }
27916         
27917         return true;
27918         
27919     },
27920     
27921     onRotateLeft : function(e)
27922     {   
27923         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27924             
27925             var minScale = this.thumbEl.getWidth() / this.minWidth;
27926             
27927             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27928             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27929             
27930             this.startScale = this.scale;
27931             
27932             while (this.getScaleLevel() < minScale){
27933             
27934                 this.scale = this.scale + 1;
27935                 
27936                 if(!this.zoomable()){
27937                     break;
27938                 }
27939                 
27940                 if(
27941                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27942                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27943                 ){
27944                     continue;
27945                 }
27946                 
27947                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27948
27949                 this.draw();
27950                 
27951                 return;
27952             }
27953             
27954             this.scale = this.startScale;
27955             
27956             this.onRotateFail();
27957             
27958             return false;
27959         }
27960         
27961         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27962
27963         if(this.isDocument){
27964             this.setThumbBoxSize();
27965             this.setThumbBoxPosition();
27966             this.setCanvasPosition();
27967         }
27968         
27969         this.draw();
27970         
27971         this.fireEvent('rotate', this, 'left');
27972         
27973     },
27974     
27975     onRotateRight : function(e)
27976     {
27977         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27978             
27979             var minScale = this.thumbEl.getWidth() / this.minWidth;
27980         
27981             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27982             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27983             
27984             this.startScale = this.scale;
27985             
27986             while (this.getScaleLevel() < minScale){
27987             
27988                 this.scale = this.scale + 1;
27989                 
27990                 if(!this.zoomable()){
27991                     break;
27992                 }
27993                 
27994                 if(
27995                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27996                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27997                 ){
27998                     continue;
27999                 }
28000                 
28001                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28002
28003                 this.draw();
28004                 
28005                 return;
28006             }
28007             
28008             this.scale = this.startScale;
28009             
28010             this.onRotateFail();
28011             
28012             return false;
28013         }
28014         
28015         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
28016
28017         if(this.isDocument){
28018             this.setThumbBoxSize();
28019             this.setThumbBoxPosition();
28020             this.setCanvasPosition();
28021         }
28022         
28023         this.draw();
28024         
28025         this.fireEvent('rotate', this, 'right');
28026     },
28027     
28028     onRotateFail : function()
28029     {
28030         this.errorEl.show(true);
28031         
28032         var _this = this;
28033         
28034         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
28035     },
28036     
28037     draw : function()
28038     {
28039         this.previewEl.dom.innerHTML = '';
28040         
28041         var canvasEl = document.createElement("canvas");
28042         
28043         var contextEl = canvasEl.getContext("2d");
28044         
28045         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28046         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28047         var center = this.imageEl.OriginWidth / 2;
28048         
28049         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28050             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28051             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28052             center = this.imageEl.OriginHeight / 2;
28053         }
28054         
28055         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28056         
28057         contextEl.translate(center, center);
28058         contextEl.rotate(this.rotate * Math.PI / 180);
28059
28060         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28061         
28062         this.canvasEl = document.createElement("canvas");
28063         
28064         this.contextEl = this.canvasEl.getContext("2d");
28065         
28066         switch (this.rotate) {
28067             case 0 :
28068                 
28069                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28070                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28071                 
28072                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28073                 
28074                 break;
28075             case 90 : 
28076                 
28077                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28078                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28079                 
28080                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28081                     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);
28082                     break;
28083                 }
28084                 
28085                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28086                 
28087                 break;
28088             case 180 :
28089                 
28090                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28091                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28092                 
28093                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28094                     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);
28095                     break;
28096                 }
28097                 
28098                 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);
28099                 
28100                 break;
28101             case 270 :
28102                 
28103                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28104                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28105         
28106                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28107                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28108                     break;
28109                 }
28110                 
28111                 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);
28112                 
28113                 break;
28114             default : 
28115                 break;
28116         }
28117         
28118         this.previewEl.appendChild(this.canvasEl);
28119         
28120         this.setCanvasPosition();
28121     },
28122     
28123     crop : function()
28124     {
28125         if(!this.canvasLoaded){
28126             return;
28127         }
28128         
28129         var imageCanvas = document.createElement("canvas");
28130         
28131         var imageContext = imageCanvas.getContext("2d");
28132         
28133         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28134         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28135         
28136         var center = imageCanvas.width / 2;
28137         
28138         imageContext.translate(center, center);
28139         
28140         imageContext.rotate(this.rotate * Math.PI / 180);
28141         
28142         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28143         
28144         var canvas = document.createElement("canvas");
28145         
28146         var context = canvas.getContext("2d");
28147                 
28148         canvas.width = this.minWidth;
28149         canvas.height = this.minHeight;
28150
28151         switch (this.rotate) {
28152             case 0 :
28153                 
28154                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28155                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28156                 
28157                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28158                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28159                 
28160                 var targetWidth = this.minWidth - 2 * x;
28161                 var targetHeight = this.minHeight - 2 * y;
28162                 
28163                 var scale = 1;
28164                 
28165                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28166                     scale = targetWidth / width;
28167                 }
28168                 
28169                 if(x > 0 && y == 0){
28170                     scale = targetHeight / height;
28171                 }
28172                 
28173                 if(x > 0 && y > 0){
28174                     scale = targetWidth / width;
28175                     
28176                     if(width < height){
28177                         scale = targetHeight / height;
28178                     }
28179                 }
28180                 
28181                 context.scale(scale, scale);
28182                 
28183                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28184                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28185
28186                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28187                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28188
28189                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28190                 
28191                 break;
28192             case 90 : 
28193                 
28194                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28195                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28196                 
28197                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28198                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28199                 
28200                 var targetWidth = this.minWidth - 2 * x;
28201                 var targetHeight = this.minHeight - 2 * y;
28202                 
28203                 var scale = 1;
28204                 
28205                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28206                     scale = targetWidth / width;
28207                 }
28208                 
28209                 if(x > 0 && y == 0){
28210                     scale = targetHeight / height;
28211                 }
28212                 
28213                 if(x > 0 && y > 0){
28214                     scale = targetWidth / width;
28215                     
28216                     if(width < height){
28217                         scale = targetHeight / height;
28218                     }
28219                 }
28220                 
28221                 context.scale(scale, scale);
28222                 
28223                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28224                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28225
28226                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28227                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28228                 
28229                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28230                 
28231                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28232                 
28233                 break;
28234             case 180 :
28235                 
28236                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28237                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28238                 
28239                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28240                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28241                 
28242                 var targetWidth = this.minWidth - 2 * x;
28243                 var targetHeight = this.minHeight - 2 * y;
28244                 
28245                 var scale = 1;
28246                 
28247                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28248                     scale = targetWidth / width;
28249                 }
28250                 
28251                 if(x > 0 && y == 0){
28252                     scale = targetHeight / height;
28253                 }
28254                 
28255                 if(x > 0 && y > 0){
28256                     scale = targetWidth / width;
28257                     
28258                     if(width < height){
28259                         scale = targetHeight / height;
28260                     }
28261                 }
28262                 
28263                 context.scale(scale, scale);
28264                 
28265                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28266                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28267
28268                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28269                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28270
28271                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28272                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28273                 
28274                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28275                 
28276                 break;
28277             case 270 :
28278                 
28279                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28280                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28281                 
28282                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28283                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28284                 
28285                 var targetWidth = this.minWidth - 2 * x;
28286                 var targetHeight = this.minHeight - 2 * y;
28287                 
28288                 var scale = 1;
28289                 
28290                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28291                     scale = targetWidth / width;
28292                 }
28293                 
28294                 if(x > 0 && y == 0){
28295                     scale = targetHeight / height;
28296                 }
28297                 
28298                 if(x > 0 && y > 0){
28299                     scale = targetWidth / width;
28300                     
28301                     if(width < height){
28302                         scale = targetHeight / height;
28303                     }
28304                 }
28305                 
28306                 context.scale(scale, scale);
28307                 
28308                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28309                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28310
28311                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28312                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28313                 
28314                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28315                 
28316                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28317                 
28318                 break;
28319             default : 
28320                 break;
28321         }
28322         
28323         this.cropData = canvas.toDataURL(this.cropType);
28324         
28325         if(this.fireEvent('crop', this, this.cropData) !== false){
28326             this.process(this.file, this.cropData);
28327         }
28328         
28329         return;
28330         
28331     },
28332     
28333     setThumbBoxSize : function()
28334     {
28335         var width, height;
28336         
28337         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28338             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28339             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28340             
28341             this.minWidth = width;
28342             this.minHeight = height;
28343             
28344             if(this.rotate == 90 || this.rotate == 270){
28345                 this.minWidth = height;
28346                 this.minHeight = width;
28347             }
28348         }
28349         
28350         height = 300;
28351         width = Math.ceil(this.minWidth * height / this.minHeight);
28352         
28353         if(this.minWidth > this.minHeight){
28354             width = 300;
28355             height = Math.ceil(this.minHeight * width / this.minWidth);
28356         }
28357         
28358         this.thumbEl.setStyle({
28359             width : width + 'px',
28360             height : height + 'px'
28361         });
28362
28363         return;
28364             
28365     },
28366     
28367     setThumbBoxPosition : function()
28368     {
28369         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28370         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28371         
28372         this.thumbEl.setLeft(x);
28373         this.thumbEl.setTop(y);
28374         
28375     },
28376     
28377     baseRotateLevel : function()
28378     {
28379         this.baseRotate = 1;
28380         
28381         if(
28382                 typeof(this.exif) != 'undefined' &&
28383                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28384                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28385         ){
28386             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28387         }
28388         
28389         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28390         
28391     },
28392     
28393     baseScaleLevel : function()
28394     {
28395         var width, height;
28396         
28397         if(this.isDocument){
28398             
28399             if(this.baseRotate == 6 || this.baseRotate == 8){
28400             
28401                 height = this.thumbEl.getHeight();
28402                 this.baseScale = height / this.imageEl.OriginWidth;
28403
28404                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28405                     width = this.thumbEl.getWidth();
28406                     this.baseScale = width / this.imageEl.OriginHeight;
28407                 }
28408
28409                 return;
28410             }
28411
28412             height = this.thumbEl.getHeight();
28413             this.baseScale = height / this.imageEl.OriginHeight;
28414
28415             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28416                 width = this.thumbEl.getWidth();
28417                 this.baseScale = width / this.imageEl.OriginWidth;
28418             }
28419
28420             return;
28421         }
28422         
28423         if(this.baseRotate == 6 || this.baseRotate == 8){
28424             
28425             width = this.thumbEl.getHeight();
28426             this.baseScale = width / this.imageEl.OriginHeight;
28427             
28428             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28429                 height = this.thumbEl.getWidth();
28430                 this.baseScale = height / this.imageEl.OriginHeight;
28431             }
28432             
28433             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28434                 height = this.thumbEl.getWidth();
28435                 this.baseScale = height / this.imageEl.OriginHeight;
28436                 
28437                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28438                     width = this.thumbEl.getHeight();
28439                     this.baseScale = width / this.imageEl.OriginWidth;
28440                 }
28441             }
28442             
28443             return;
28444         }
28445         
28446         width = this.thumbEl.getWidth();
28447         this.baseScale = width / this.imageEl.OriginWidth;
28448         
28449         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28450             height = this.thumbEl.getHeight();
28451             this.baseScale = height / this.imageEl.OriginHeight;
28452         }
28453         
28454         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28455             
28456             height = this.thumbEl.getHeight();
28457             this.baseScale = height / this.imageEl.OriginHeight;
28458             
28459             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28460                 width = this.thumbEl.getWidth();
28461                 this.baseScale = width / this.imageEl.OriginWidth;
28462             }
28463             
28464         }
28465         
28466         return;
28467     },
28468     
28469     getScaleLevel : function()
28470     {
28471         return this.baseScale * Math.pow(1.1, this.scale);
28472     },
28473     
28474     onTouchStart : function(e)
28475     {
28476         if(!this.canvasLoaded){
28477             this.beforeSelectFile(e);
28478             return;
28479         }
28480         
28481         var touches = e.browserEvent.touches;
28482         
28483         if(!touches){
28484             return;
28485         }
28486         
28487         if(touches.length == 1){
28488             this.onMouseDown(e);
28489             return;
28490         }
28491         
28492         if(touches.length != 2){
28493             return;
28494         }
28495         
28496         var coords = [];
28497         
28498         for(var i = 0, finger; finger = touches[i]; i++){
28499             coords.push(finger.pageX, finger.pageY);
28500         }
28501         
28502         var x = Math.pow(coords[0] - coords[2], 2);
28503         var y = Math.pow(coords[1] - coords[3], 2);
28504         
28505         this.startDistance = Math.sqrt(x + y);
28506         
28507         this.startScale = this.scale;
28508         
28509         this.pinching = true;
28510         this.dragable = false;
28511         
28512     },
28513     
28514     onTouchMove : function(e)
28515     {
28516         if(!this.pinching && !this.dragable){
28517             return;
28518         }
28519         
28520         var touches = e.browserEvent.touches;
28521         
28522         if(!touches){
28523             return;
28524         }
28525         
28526         if(this.dragable){
28527             this.onMouseMove(e);
28528             return;
28529         }
28530         
28531         var coords = [];
28532         
28533         for(var i = 0, finger; finger = touches[i]; i++){
28534             coords.push(finger.pageX, finger.pageY);
28535         }
28536         
28537         var x = Math.pow(coords[0] - coords[2], 2);
28538         var y = Math.pow(coords[1] - coords[3], 2);
28539         
28540         this.endDistance = Math.sqrt(x + y);
28541         
28542         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28543         
28544         if(!this.zoomable()){
28545             this.scale = this.startScale;
28546             return;
28547         }
28548         
28549         this.draw();
28550         
28551     },
28552     
28553     onTouchEnd : function(e)
28554     {
28555         this.pinching = false;
28556         this.dragable = false;
28557         
28558     },
28559     
28560     process : function(file, crop)
28561     {
28562         if(this.loadMask){
28563             this.maskEl.mask(this.loadingText);
28564         }
28565         
28566         this.xhr = new XMLHttpRequest();
28567         
28568         file.xhr = this.xhr;
28569
28570         this.xhr.open(this.method, this.url, true);
28571         
28572         var headers = {
28573             "Accept": "application/json",
28574             "Cache-Control": "no-cache",
28575             "X-Requested-With": "XMLHttpRequest"
28576         };
28577         
28578         for (var headerName in headers) {
28579             var headerValue = headers[headerName];
28580             if (headerValue) {
28581                 this.xhr.setRequestHeader(headerName, headerValue);
28582             }
28583         }
28584         
28585         var _this = this;
28586         
28587         this.xhr.onload = function()
28588         {
28589             _this.xhrOnLoad(_this.xhr);
28590         }
28591         
28592         this.xhr.onerror = function()
28593         {
28594             _this.xhrOnError(_this.xhr);
28595         }
28596         
28597         var formData = new FormData();
28598
28599         formData.append('returnHTML', 'NO');
28600         
28601         if(crop){
28602             formData.append('crop', crop);
28603         }
28604         
28605         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28606             formData.append(this.paramName, file, file.name);
28607         }
28608         
28609         if(typeof(file.filename) != 'undefined'){
28610             formData.append('filename', file.filename);
28611         }
28612         
28613         if(typeof(file.mimetype) != 'undefined'){
28614             formData.append('mimetype', file.mimetype);
28615         }
28616         
28617         if(this.fireEvent('arrange', this, formData) != false){
28618             this.xhr.send(formData);
28619         };
28620     },
28621     
28622     xhrOnLoad : function(xhr)
28623     {
28624         if(this.loadMask){
28625             this.maskEl.unmask();
28626         }
28627         
28628         if (xhr.readyState !== 4) {
28629             this.fireEvent('exception', this, xhr);
28630             return;
28631         }
28632
28633         var response = Roo.decode(xhr.responseText);
28634         
28635         if(!response.success){
28636             this.fireEvent('exception', this, xhr);
28637             return;
28638         }
28639         
28640         var response = Roo.decode(xhr.responseText);
28641         
28642         this.fireEvent('upload', this, response);
28643         
28644     },
28645     
28646     xhrOnError : function()
28647     {
28648         if(this.loadMask){
28649             this.maskEl.unmask();
28650         }
28651         
28652         Roo.log('xhr on error');
28653         
28654         var response = Roo.decode(xhr.responseText);
28655           
28656         Roo.log(response);
28657         
28658     },
28659     
28660     prepare : function(file)
28661     {   
28662         if(this.loadMask){
28663             this.maskEl.mask(this.loadingText);
28664         }
28665         
28666         this.file = false;
28667         this.exif = {};
28668         
28669         if(typeof(file) === 'string'){
28670             this.loadCanvas(file);
28671             return;
28672         }
28673         
28674         if(!file || !this.urlAPI){
28675             return;
28676         }
28677         
28678         this.file = file;
28679         this.cropType = file.type;
28680         
28681         var _this = this;
28682         
28683         if(this.fireEvent('prepare', this, this.file) != false){
28684             
28685             var reader = new FileReader();
28686             
28687             reader.onload = function (e) {
28688                 if (e.target.error) {
28689                     Roo.log(e.target.error);
28690                     return;
28691                 }
28692                 
28693                 var buffer = e.target.result,
28694                     dataView = new DataView(buffer),
28695                     offset = 2,
28696                     maxOffset = dataView.byteLength - 4,
28697                     markerBytes,
28698                     markerLength;
28699                 
28700                 if (dataView.getUint16(0) === 0xffd8) {
28701                     while (offset < maxOffset) {
28702                         markerBytes = dataView.getUint16(offset);
28703                         
28704                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28705                             markerLength = dataView.getUint16(offset + 2) + 2;
28706                             if (offset + markerLength > dataView.byteLength) {
28707                                 Roo.log('Invalid meta data: Invalid segment size.');
28708                                 break;
28709                             }
28710                             
28711                             if(markerBytes == 0xffe1){
28712                                 _this.parseExifData(
28713                                     dataView,
28714                                     offset,
28715                                     markerLength
28716                                 );
28717                             }
28718                             
28719                             offset += markerLength;
28720                             
28721                             continue;
28722                         }
28723                         
28724                         break;
28725                     }
28726                     
28727                 }
28728                 
28729                 var url = _this.urlAPI.createObjectURL(_this.file);
28730                 
28731                 _this.loadCanvas(url);
28732                 
28733                 return;
28734             }
28735             
28736             reader.readAsArrayBuffer(this.file);
28737             
28738         }
28739         
28740     },
28741     
28742     parseExifData : function(dataView, offset, length)
28743     {
28744         var tiffOffset = offset + 10,
28745             littleEndian,
28746             dirOffset;
28747     
28748         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28749             // No Exif data, might be XMP data instead
28750             return;
28751         }
28752         
28753         // Check for the ASCII code for "Exif" (0x45786966):
28754         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28755             // No Exif data, might be XMP data instead
28756             return;
28757         }
28758         if (tiffOffset + 8 > dataView.byteLength) {
28759             Roo.log('Invalid Exif data: Invalid segment size.');
28760             return;
28761         }
28762         // Check for the two null bytes:
28763         if (dataView.getUint16(offset + 8) !== 0x0000) {
28764             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28765             return;
28766         }
28767         // Check the byte alignment:
28768         switch (dataView.getUint16(tiffOffset)) {
28769         case 0x4949:
28770             littleEndian = true;
28771             break;
28772         case 0x4D4D:
28773             littleEndian = false;
28774             break;
28775         default:
28776             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28777             return;
28778         }
28779         // Check for the TIFF tag marker (0x002A):
28780         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28781             Roo.log('Invalid Exif data: Missing TIFF marker.');
28782             return;
28783         }
28784         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28785         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28786         
28787         this.parseExifTags(
28788             dataView,
28789             tiffOffset,
28790             tiffOffset + dirOffset,
28791             littleEndian
28792         );
28793     },
28794     
28795     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28796     {
28797         var tagsNumber,
28798             dirEndOffset,
28799             i;
28800         if (dirOffset + 6 > dataView.byteLength) {
28801             Roo.log('Invalid Exif data: Invalid directory offset.');
28802             return;
28803         }
28804         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28805         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28806         if (dirEndOffset + 4 > dataView.byteLength) {
28807             Roo.log('Invalid Exif data: Invalid directory size.');
28808             return;
28809         }
28810         for (i = 0; i < tagsNumber; i += 1) {
28811             this.parseExifTag(
28812                 dataView,
28813                 tiffOffset,
28814                 dirOffset + 2 + 12 * i, // tag offset
28815                 littleEndian
28816             );
28817         }
28818         // Return the offset to the next directory:
28819         return dataView.getUint32(dirEndOffset, littleEndian);
28820     },
28821     
28822     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28823     {
28824         var tag = dataView.getUint16(offset, littleEndian);
28825         
28826         this.exif[tag] = this.getExifValue(
28827             dataView,
28828             tiffOffset,
28829             offset,
28830             dataView.getUint16(offset + 2, littleEndian), // tag type
28831             dataView.getUint32(offset + 4, littleEndian), // tag length
28832             littleEndian
28833         );
28834     },
28835     
28836     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28837     {
28838         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28839             tagSize,
28840             dataOffset,
28841             values,
28842             i,
28843             str,
28844             c;
28845     
28846         if (!tagType) {
28847             Roo.log('Invalid Exif data: Invalid tag type.');
28848             return;
28849         }
28850         
28851         tagSize = tagType.size * length;
28852         // Determine if the value is contained in the dataOffset bytes,
28853         // or if the value at the dataOffset is a pointer to the actual data:
28854         dataOffset = tagSize > 4 ?
28855                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28856         if (dataOffset + tagSize > dataView.byteLength) {
28857             Roo.log('Invalid Exif data: Invalid data offset.');
28858             return;
28859         }
28860         if (length === 1) {
28861             return tagType.getValue(dataView, dataOffset, littleEndian);
28862         }
28863         values = [];
28864         for (i = 0; i < length; i += 1) {
28865             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28866         }
28867         
28868         if (tagType.ascii) {
28869             str = '';
28870             // Concatenate the chars:
28871             for (i = 0; i < values.length; i += 1) {
28872                 c = values[i];
28873                 // Ignore the terminating NULL byte(s):
28874                 if (c === '\u0000') {
28875                     break;
28876                 }
28877                 str += c;
28878             }
28879             return str;
28880         }
28881         return values;
28882     }
28883     
28884 });
28885
28886 Roo.apply(Roo.bootstrap.UploadCropbox, {
28887     tags : {
28888         'Orientation': 0x0112
28889     },
28890     
28891     Orientation: {
28892             1: 0, //'top-left',
28893 //            2: 'top-right',
28894             3: 180, //'bottom-right',
28895 //            4: 'bottom-left',
28896 //            5: 'left-top',
28897             6: 90, //'right-top',
28898 //            7: 'right-bottom',
28899             8: 270 //'left-bottom'
28900     },
28901     
28902     exifTagTypes : {
28903         // byte, 8-bit unsigned int:
28904         1: {
28905             getValue: function (dataView, dataOffset) {
28906                 return dataView.getUint8(dataOffset);
28907             },
28908             size: 1
28909         },
28910         // ascii, 8-bit byte:
28911         2: {
28912             getValue: function (dataView, dataOffset) {
28913                 return String.fromCharCode(dataView.getUint8(dataOffset));
28914             },
28915             size: 1,
28916             ascii: true
28917         },
28918         // short, 16 bit int:
28919         3: {
28920             getValue: function (dataView, dataOffset, littleEndian) {
28921                 return dataView.getUint16(dataOffset, littleEndian);
28922             },
28923             size: 2
28924         },
28925         // long, 32 bit int:
28926         4: {
28927             getValue: function (dataView, dataOffset, littleEndian) {
28928                 return dataView.getUint32(dataOffset, littleEndian);
28929             },
28930             size: 4
28931         },
28932         // rational = two long values, first is numerator, second is denominator:
28933         5: {
28934             getValue: function (dataView, dataOffset, littleEndian) {
28935                 return dataView.getUint32(dataOffset, littleEndian) /
28936                     dataView.getUint32(dataOffset + 4, littleEndian);
28937             },
28938             size: 8
28939         },
28940         // slong, 32 bit signed int:
28941         9: {
28942             getValue: function (dataView, dataOffset, littleEndian) {
28943                 return dataView.getInt32(dataOffset, littleEndian);
28944             },
28945             size: 4
28946         },
28947         // srational, two slongs, first is numerator, second is denominator:
28948         10: {
28949             getValue: function (dataView, dataOffset, littleEndian) {
28950                 return dataView.getInt32(dataOffset, littleEndian) /
28951                     dataView.getInt32(dataOffset + 4, littleEndian);
28952             },
28953             size: 8
28954         }
28955     },
28956     
28957     footer : {
28958         STANDARD : [
28959             {
28960                 tag : 'div',
28961                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28962                 action : 'rotate-left',
28963                 cn : [
28964                     {
28965                         tag : 'button',
28966                         cls : 'btn btn-default',
28967                         html : '<i class="fa fa-undo"></i>'
28968                     }
28969                 ]
28970             },
28971             {
28972                 tag : 'div',
28973                 cls : 'btn-group roo-upload-cropbox-picture',
28974                 action : 'picture',
28975                 cn : [
28976                     {
28977                         tag : 'button',
28978                         cls : 'btn btn-default',
28979                         html : '<i class="fa fa-picture-o"></i>'
28980                     }
28981                 ]
28982             },
28983             {
28984                 tag : 'div',
28985                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28986                 action : 'rotate-right',
28987                 cn : [
28988                     {
28989                         tag : 'button',
28990                         cls : 'btn btn-default',
28991                         html : '<i class="fa fa-repeat"></i>'
28992                     }
28993                 ]
28994             }
28995         ],
28996         DOCUMENT : [
28997             {
28998                 tag : 'div',
28999                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29000                 action : 'rotate-left',
29001                 cn : [
29002                     {
29003                         tag : 'button',
29004                         cls : 'btn btn-default',
29005                         html : '<i class="fa fa-undo"></i>'
29006                     }
29007                 ]
29008             },
29009             {
29010                 tag : 'div',
29011                 cls : 'btn-group roo-upload-cropbox-download',
29012                 action : 'download',
29013                 cn : [
29014                     {
29015                         tag : 'button',
29016                         cls : 'btn btn-default',
29017                         html : '<i class="fa fa-download"></i>'
29018                     }
29019                 ]
29020             },
29021             {
29022                 tag : 'div',
29023                 cls : 'btn-group roo-upload-cropbox-crop',
29024                 action : 'crop',
29025                 cn : [
29026                     {
29027                         tag : 'button',
29028                         cls : 'btn btn-default',
29029                         html : '<i class="fa fa-crop"></i>'
29030                     }
29031                 ]
29032             },
29033             {
29034                 tag : 'div',
29035                 cls : 'btn-group roo-upload-cropbox-trash',
29036                 action : 'trash',
29037                 cn : [
29038                     {
29039                         tag : 'button',
29040                         cls : 'btn btn-default',
29041                         html : '<i class="fa fa-trash"></i>'
29042                     }
29043                 ]
29044             },
29045             {
29046                 tag : 'div',
29047                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29048                 action : 'rotate-right',
29049                 cn : [
29050                     {
29051                         tag : 'button',
29052                         cls : 'btn btn-default',
29053                         html : '<i class="fa fa-repeat"></i>'
29054                     }
29055                 ]
29056             }
29057         ],
29058         ROTATOR : [
29059             {
29060                 tag : 'div',
29061                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29062                 action : 'rotate-left',
29063                 cn : [
29064                     {
29065                         tag : 'button',
29066                         cls : 'btn btn-default',
29067                         html : '<i class="fa fa-undo"></i>'
29068                     }
29069                 ]
29070             },
29071             {
29072                 tag : 'div',
29073                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29074                 action : 'rotate-right',
29075                 cn : [
29076                     {
29077                         tag : 'button',
29078                         cls : 'btn btn-default',
29079                         html : '<i class="fa fa-repeat"></i>'
29080                     }
29081                 ]
29082             }
29083         ]
29084     }
29085 });
29086
29087 /*
29088 * Licence: LGPL
29089 */
29090
29091 /**
29092  * @class Roo.bootstrap.DocumentManager
29093  * @extends Roo.bootstrap.Component
29094  * Bootstrap DocumentManager class
29095  * @cfg {String} paramName default 'imageUpload'
29096  * @cfg {String} toolTipName default 'filename'
29097  * @cfg {String} method default POST
29098  * @cfg {String} url action url
29099  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29100  * @cfg {Boolean} multiple multiple upload default true
29101  * @cfg {Number} thumbSize default 300
29102  * @cfg {String} fieldLabel
29103  * @cfg {Number} labelWidth default 4
29104  * @cfg {String} labelAlign (left|top) default left
29105  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29106 * @cfg {Number} labellg set the width of label (1-12)
29107  * @cfg {Number} labelmd set the width of label (1-12)
29108  * @cfg {Number} labelsm set the width of label (1-12)
29109  * @cfg {Number} labelxs set the width of label (1-12)
29110  * 
29111  * @constructor
29112  * Create a new DocumentManager
29113  * @param {Object} config The config object
29114  */
29115
29116 Roo.bootstrap.DocumentManager = function(config){
29117     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29118     
29119     this.files = [];
29120     this.delegates = [];
29121     
29122     this.addEvents({
29123         /**
29124          * @event initial
29125          * Fire when initial the DocumentManager
29126          * @param {Roo.bootstrap.DocumentManager} this
29127          */
29128         "initial" : true,
29129         /**
29130          * @event inspect
29131          * inspect selected file
29132          * @param {Roo.bootstrap.DocumentManager} this
29133          * @param {File} file
29134          */
29135         "inspect" : true,
29136         /**
29137          * @event exception
29138          * Fire when xhr load exception
29139          * @param {Roo.bootstrap.DocumentManager} this
29140          * @param {XMLHttpRequest} xhr
29141          */
29142         "exception" : true,
29143         /**
29144          * @event afterupload
29145          * Fire when xhr load exception
29146          * @param {Roo.bootstrap.DocumentManager} this
29147          * @param {XMLHttpRequest} xhr
29148          */
29149         "afterupload" : true,
29150         /**
29151          * @event prepare
29152          * prepare the form data
29153          * @param {Roo.bootstrap.DocumentManager} this
29154          * @param {Object} formData
29155          */
29156         "prepare" : true,
29157         /**
29158          * @event remove
29159          * Fire when remove the file
29160          * @param {Roo.bootstrap.DocumentManager} this
29161          * @param {Object} file
29162          */
29163         "remove" : true,
29164         /**
29165          * @event refresh
29166          * Fire after refresh the file
29167          * @param {Roo.bootstrap.DocumentManager} this
29168          */
29169         "refresh" : true,
29170         /**
29171          * @event click
29172          * Fire after click the image
29173          * @param {Roo.bootstrap.DocumentManager} this
29174          * @param {Object} file
29175          */
29176         "click" : true,
29177         /**
29178          * @event edit
29179          * Fire when upload a image and editable set to true
29180          * @param {Roo.bootstrap.DocumentManager} this
29181          * @param {Object} file
29182          */
29183         "edit" : true,
29184         /**
29185          * @event beforeselectfile
29186          * Fire before select file
29187          * @param {Roo.bootstrap.DocumentManager} this
29188          */
29189         "beforeselectfile" : true,
29190         /**
29191          * @event process
29192          * Fire before process file
29193          * @param {Roo.bootstrap.DocumentManager} this
29194          * @param {Object} file
29195          */
29196         "process" : true,
29197         /**
29198          * @event previewrendered
29199          * Fire when preview rendered
29200          * @param {Roo.bootstrap.DocumentManager} this
29201          * @param {Object} file
29202          */
29203         "previewrendered" : true,
29204         /**
29205          */
29206         "previewResize" : true
29207         
29208     });
29209 };
29210
29211 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29212     
29213     boxes : 0,
29214     inputName : '',
29215     thumbSize : 300,
29216     multiple : true,
29217     files : false,
29218     method : 'POST',
29219     url : '',
29220     paramName : 'imageUpload',
29221     toolTipName : 'filename',
29222     fieldLabel : '',
29223     labelWidth : 4,
29224     labelAlign : 'left',
29225     editable : true,
29226     delegates : false,
29227     xhr : false, 
29228     
29229     labellg : 0,
29230     labelmd : 0,
29231     labelsm : 0,
29232     labelxs : 0,
29233     
29234     getAutoCreate : function()
29235     {   
29236         var managerWidget = {
29237             tag : 'div',
29238             cls : 'roo-document-manager',
29239             cn : [
29240                 {
29241                     tag : 'input',
29242                     cls : 'roo-document-manager-selector',
29243                     type : 'file'
29244                 },
29245                 {
29246                     tag : 'div',
29247                     cls : 'roo-document-manager-uploader',
29248                     cn : [
29249                         {
29250                             tag : 'div',
29251                             cls : 'roo-document-manager-upload-btn',
29252                             html : '<i class="fa fa-plus"></i>'
29253                         }
29254                     ]
29255                     
29256                 }
29257             ]
29258         };
29259         
29260         var content = [
29261             {
29262                 tag : 'div',
29263                 cls : 'column col-md-12',
29264                 cn : managerWidget
29265             }
29266         ];
29267         
29268         if(this.fieldLabel.length){
29269             
29270             content = [
29271                 {
29272                     tag : 'div',
29273                     cls : 'column col-md-12',
29274                     html : this.fieldLabel
29275                 },
29276                 {
29277                     tag : 'div',
29278                     cls : 'column col-md-12',
29279                     cn : managerWidget
29280                 }
29281             ];
29282
29283             if(this.labelAlign == 'left'){
29284                 content = [
29285                     {
29286                         tag : 'div',
29287                         cls : 'column',
29288                         html : this.fieldLabel
29289                     },
29290                     {
29291                         tag : 'div',
29292                         cls : 'column',
29293                         cn : managerWidget
29294                     }
29295                 ];
29296                 
29297                 if(this.labelWidth > 12){
29298                     content[0].style = "width: " + this.labelWidth + 'px';
29299                 }
29300
29301                 if(this.labelWidth < 13 && this.labelmd == 0){
29302                     this.labelmd = this.labelWidth;
29303                 }
29304
29305                 if(this.labellg > 0){
29306                     content[0].cls += ' col-lg-' + this.labellg;
29307                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29308                 }
29309
29310                 if(this.labelmd > 0){
29311                     content[0].cls += ' col-md-' + this.labelmd;
29312                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29313                 }
29314
29315                 if(this.labelsm > 0){
29316                     content[0].cls += ' col-sm-' + this.labelsm;
29317                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29318                 }
29319
29320                 if(this.labelxs > 0){
29321                     content[0].cls += ' col-xs-' + this.labelxs;
29322                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29323                 }
29324                 
29325             }
29326         }
29327         
29328         var cfg = {
29329             tag : 'div',
29330             cls : 'row clearfix',
29331             cn : content
29332         };
29333         
29334         return cfg;
29335         
29336     },
29337     
29338     initEvents : function()
29339     {
29340         this.managerEl = this.el.select('.roo-document-manager', true).first();
29341         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29342         
29343         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29344         this.selectorEl.hide();
29345         
29346         if(this.multiple){
29347             this.selectorEl.attr('multiple', 'multiple');
29348         }
29349         
29350         this.selectorEl.on('change', this.onFileSelected, this);
29351         
29352         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29353         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29354         
29355         this.uploader.on('click', this.onUploaderClick, this);
29356         
29357         this.renderProgressDialog();
29358         
29359         var _this = this;
29360         
29361         window.addEventListener("resize", function() { _this.refresh(); } );
29362         
29363         this.fireEvent('initial', this);
29364     },
29365     
29366     renderProgressDialog : function()
29367     {
29368         var _this = this;
29369         
29370         this.progressDialog = new Roo.bootstrap.Modal({
29371             cls : 'roo-document-manager-progress-dialog',
29372             allow_close : false,
29373             animate : false,
29374             title : '',
29375             buttons : [
29376                 {
29377                     name  :'cancel',
29378                     weight : 'danger',
29379                     html : 'Cancel'
29380                 }
29381             ], 
29382             listeners : { 
29383                 btnclick : function() {
29384                     _this.uploadCancel();
29385                     this.hide();
29386                 }
29387             }
29388         });
29389          
29390         this.progressDialog.render(Roo.get(document.body));
29391          
29392         this.progress = new Roo.bootstrap.Progress({
29393             cls : 'roo-document-manager-progress',
29394             active : true,
29395             striped : true
29396         });
29397         
29398         this.progress.render(this.progressDialog.getChildContainer());
29399         
29400         this.progressBar = new Roo.bootstrap.ProgressBar({
29401             cls : 'roo-document-manager-progress-bar',
29402             aria_valuenow : 0,
29403             aria_valuemin : 0,
29404             aria_valuemax : 12,
29405             panel : 'success'
29406         });
29407         
29408         this.progressBar.render(this.progress.getChildContainer());
29409     },
29410     
29411     onUploaderClick : function(e)
29412     {
29413         e.preventDefault();
29414      
29415         if(this.fireEvent('beforeselectfile', this) != false){
29416             this.selectorEl.dom.click();
29417         }
29418         
29419     },
29420     
29421     onFileSelected : function(e)
29422     {
29423         e.preventDefault();
29424         
29425         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29426             return;
29427         }
29428         
29429         Roo.each(this.selectorEl.dom.files, function(file){
29430             if(this.fireEvent('inspect', this, file) != false){
29431                 this.files.push(file);
29432             }
29433         }, this);
29434         
29435         this.queue();
29436         
29437     },
29438     
29439     queue : function()
29440     {
29441         this.selectorEl.dom.value = '';
29442         
29443         if(!this.files || !this.files.length){
29444             return;
29445         }
29446         
29447         if(this.boxes > 0 && this.files.length > this.boxes){
29448             this.files = this.files.slice(0, this.boxes);
29449         }
29450         
29451         this.uploader.show();
29452         
29453         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29454             this.uploader.hide();
29455         }
29456         
29457         var _this = this;
29458         
29459         var files = [];
29460         
29461         var docs = [];
29462         
29463         Roo.each(this.files, function(file){
29464             
29465             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29466                 var f = this.renderPreview(file);
29467                 files.push(f);
29468                 return;
29469             }
29470             
29471             if(file.type.indexOf('image') != -1){
29472                 this.delegates.push(
29473                     (function(){
29474                         _this.process(file);
29475                     }).createDelegate(this)
29476                 );
29477         
29478                 return;
29479             }
29480             
29481             docs.push(
29482                 (function(){
29483                     _this.process(file);
29484                 }).createDelegate(this)
29485             );
29486             
29487         }, this);
29488         
29489         this.files = files;
29490         
29491         this.delegates = this.delegates.concat(docs);
29492         
29493         if(!this.delegates.length){
29494             this.refresh();
29495             return;
29496         }
29497         
29498         this.progressBar.aria_valuemax = this.delegates.length;
29499         
29500         this.arrange();
29501         
29502         return;
29503     },
29504     
29505     arrange : function()
29506     {
29507         if(!this.delegates.length){
29508             this.progressDialog.hide();
29509             this.refresh();
29510             return;
29511         }
29512         
29513         var delegate = this.delegates.shift();
29514         
29515         this.progressDialog.show();
29516         
29517         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29518         
29519         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29520         
29521         delegate();
29522     },
29523     
29524     refresh : function()
29525     {
29526         this.uploader.show();
29527         
29528         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29529             this.uploader.hide();
29530         }
29531         
29532         Roo.isTouch ? this.closable(false) : this.closable(true);
29533         
29534         this.fireEvent('refresh', this);
29535     },
29536     
29537     onRemove : function(e, el, o)
29538     {
29539         e.preventDefault();
29540         
29541         this.fireEvent('remove', this, o);
29542         
29543     },
29544     
29545     remove : function(o)
29546     {
29547         var files = [];
29548         
29549         Roo.each(this.files, function(file){
29550             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29551                 files.push(file);
29552                 return;
29553             }
29554
29555             o.target.remove();
29556
29557         }, this);
29558         
29559         this.files = files;
29560         
29561         this.refresh();
29562     },
29563     
29564     clear : function()
29565     {
29566         Roo.each(this.files, function(file){
29567             if(!file.target){
29568                 return;
29569             }
29570             
29571             file.target.remove();
29572
29573         }, this);
29574         
29575         this.files = [];
29576         
29577         this.refresh();
29578     },
29579     
29580     onClick : function(e, el, o)
29581     {
29582         e.preventDefault();
29583         
29584         this.fireEvent('click', this, o);
29585         
29586     },
29587     
29588     closable : function(closable)
29589     {
29590         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29591             
29592             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29593             
29594             if(closable){
29595                 el.show();
29596                 return;
29597             }
29598             
29599             el.hide();
29600             
29601         }, this);
29602     },
29603     
29604     xhrOnLoad : function(xhr)
29605     {
29606         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29607             el.remove();
29608         }, this);
29609         
29610         if (xhr.readyState !== 4) {
29611             this.arrange();
29612             this.fireEvent('exception', this, xhr);
29613             return;
29614         }
29615
29616         var response = Roo.decode(xhr.responseText);
29617         
29618         if(!response.success){
29619             this.arrange();
29620             this.fireEvent('exception', this, xhr);
29621             return;
29622         }
29623         
29624         var file = this.renderPreview(response.data);
29625         
29626         this.files.push(file);
29627         
29628         this.arrange();
29629         
29630         this.fireEvent('afterupload', this, xhr);
29631         
29632     },
29633     
29634     xhrOnError : function(xhr)
29635     {
29636         Roo.log('xhr on error');
29637         
29638         var response = Roo.decode(xhr.responseText);
29639           
29640         Roo.log(response);
29641         
29642         this.arrange();
29643     },
29644     
29645     process : function(file)
29646     {
29647         if(this.fireEvent('process', this, file) !== false){
29648             if(this.editable && file.type.indexOf('image') != -1){
29649                 this.fireEvent('edit', this, file);
29650                 return;
29651             }
29652
29653             this.uploadStart(file, false);
29654
29655             return;
29656         }
29657         
29658     },
29659     
29660     uploadStart : function(file, crop)
29661     {
29662         this.xhr = new XMLHttpRequest();
29663         
29664         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29665             this.arrange();
29666             return;
29667         }
29668         
29669         file.xhr = this.xhr;
29670             
29671         this.managerEl.createChild({
29672             tag : 'div',
29673             cls : 'roo-document-manager-loading',
29674             cn : [
29675                 {
29676                     tag : 'div',
29677                     tooltip : file.name,
29678                     cls : 'roo-document-manager-thumb',
29679                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29680                 }
29681             ]
29682
29683         });
29684
29685         this.xhr.open(this.method, this.url, true);
29686         
29687         var headers = {
29688             "Accept": "application/json",
29689             "Cache-Control": "no-cache",
29690             "X-Requested-With": "XMLHttpRequest"
29691         };
29692         
29693         for (var headerName in headers) {
29694             var headerValue = headers[headerName];
29695             if (headerValue) {
29696                 this.xhr.setRequestHeader(headerName, headerValue);
29697             }
29698         }
29699         
29700         var _this = this;
29701         
29702         this.xhr.onload = function()
29703         {
29704             _this.xhrOnLoad(_this.xhr);
29705         }
29706         
29707         this.xhr.onerror = function()
29708         {
29709             _this.xhrOnError(_this.xhr);
29710         }
29711         
29712         var formData = new FormData();
29713
29714         formData.append('returnHTML', 'NO');
29715         
29716         if(crop){
29717             formData.append('crop', crop);
29718         }
29719         
29720         formData.append(this.paramName, file, file.name);
29721         
29722         var options = {
29723             file : file, 
29724             manually : false
29725         };
29726         
29727         if(this.fireEvent('prepare', this, formData, options) != false){
29728             
29729             if(options.manually){
29730                 return;
29731             }
29732             
29733             this.xhr.send(formData);
29734             return;
29735         };
29736         
29737         this.uploadCancel();
29738     },
29739     
29740     uploadCancel : function()
29741     {
29742         if (this.xhr) {
29743             this.xhr.abort();
29744         }
29745         
29746         this.delegates = [];
29747         
29748         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29749             el.remove();
29750         }, this);
29751         
29752         this.arrange();
29753     },
29754     
29755     renderPreview : function(file)
29756     {
29757         if(typeof(file.target) != 'undefined' && file.target){
29758             return file;
29759         }
29760         
29761         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29762         
29763         var previewEl = this.managerEl.createChild({
29764             tag : 'div',
29765             cls : 'roo-document-manager-preview',
29766             cn : [
29767                 {
29768                     tag : 'div',
29769                     tooltip : file[this.toolTipName],
29770                     cls : 'roo-document-manager-thumb',
29771                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29772                 },
29773                 {
29774                     tag : 'button',
29775                     cls : 'close',
29776                     html : '<i class="fa fa-times-circle"></i>'
29777                 }
29778             ]
29779         });
29780
29781         var close = previewEl.select('button.close', true).first();
29782
29783         close.on('click', this.onRemove, this, file);
29784
29785         file.target = previewEl;
29786
29787         var image = previewEl.select('img', true).first();
29788         
29789         var _this = this;
29790         
29791         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29792         
29793         image.on('click', this.onClick, this, file);
29794         
29795         this.fireEvent('previewrendered', this, file);
29796         
29797         return file;
29798         
29799     },
29800     
29801     onPreviewLoad : function(file, image)
29802     {
29803         if(typeof(file.target) == 'undefined' || !file.target){
29804             return;
29805         }
29806         
29807         var width = image.dom.naturalWidth || image.dom.width;
29808         var height = image.dom.naturalHeight || image.dom.height;
29809         
29810         if(!this.previewResize) {
29811             return;
29812         }
29813         
29814         if(width > height){
29815             file.target.addClass('wide');
29816             return;
29817         }
29818         
29819         file.target.addClass('tall');
29820         return;
29821         
29822     },
29823     
29824     uploadFromSource : function(file, crop)
29825     {
29826         this.xhr = new XMLHttpRequest();
29827         
29828         this.managerEl.createChild({
29829             tag : 'div',
29830             cls : 'roo-document-manager-loading',
29831             cn : [
29832                 {
29833                     tag : 'div',
29834                     tooltip : file.name,
29835                     cls : 'roo-document-manager-thumb',
29836                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29837                 }
29838             ]
29839
29840         });
29841
29842         this.xhr.open(this.method, this.url, true);
29843         
29844         var headers = {
29845             "Accept": "application/json",
29846             "Cache-Control": "no-cache",
29847             "X-Requested-With": "XMLHttpRequest"
29848         };
29849         
29850         for (var headerName in headers) {
29851             var headerValue = headers[headerName];
29852             if (headerValue) {
29853                 this.xhr.setRequestHeader(headerName, headerValue);
29854             }
29855         }
29856         
29857         var _this = this;
29858         
29859         this.xhr.onload = function()
29860         {
29861             _this.xhrOnLoad(_this.xhr);
29862         }
29863         
29864         this.xhr.onerror = function()
29865         {
29866             _this.xhrOnError(_this.xhr);
29867         }
29868         
29869         var formData = new FormData();
29870
29871         formData.append('returnHTML', 'NO');
29872         
29873         formData.append('crop', crop);
29874         
29875         if(typeof(file.filename) != 'undefined'){
29876             formData.append('filename', file.filename);
29877         }
29878         
29879         if(typeof(file.mimetype) != 'undefined'){
29880             formData.append('mimetype', file.mimetype);
29881         }
29882         
29883         Roo.log(formData);
29884         
29885         if(this.fireEvent('prepare', this, formData) != false){
29886             this.xhr.send(formData);
29887         };
29888     }
29889 });
29890
29891 /*
29892 * Licence: LGPL
29893 */
29894
29895 /**
29896  * @class Roo.bootstrap.DocumentViewer
29897  * @extends Roo.bootstrap.Component
29898  * Bootstrap DocumentViewer class
29899  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29900  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29901  * 
29902  * @constructor
29903  * Create a new DocumentViewer
29904  * @param {Object} config The config object
29905  */
29906
29907 Roo.bootstrap.DocumentViewer = function(config){
29908     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29909     
29910     this.addEvents({
29911         /**
29912          * @event initial
29913          * Fire after initEvent
29914          * @param {Roo.bootstrap.DocumentViewer} this
29915          */
29916         "initial" : true,
29917         /**
29918          * @event click
29919          * Fire after click
29920          * @param {Roo.bootstrap.DocumentViewer} this
29921          */
29922         "click" : true,
29923         /**
29924          * @event download
29925          * Fire after download button
29926          * @param {Roo.bootstrap.DocumentViewer} this
29927          */
29928         "download" : true,
29929         /**
29930          * @event trash
29931          * Fire after trash button
29932          * @param {Roo.bootstrap.DocumentViewer} this
29933          */
29934         "trash" : true
29935         
29936     });
29937 };
29938
29939 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29940     
29941     showDownload : true,
29942     
29943     showTrash : true,
29944     
29945     getAutoCreate : function()
29946     {
29947         var cfg = {
29948             tag : 'div',
29949             cls : 'roo-document-viewer',
29950             cn : [
29951                 {
29952                     tag : 'div',
29953                     cls : 'roo-document-viewer-body',
29954                     cn : [
29955                         {
29956                             tag : 'div',
29957                             cls : 'roo-document-viewer-thumb',
29958                             cn : [
29959                                 {
29960                                     tag : 'img',
29961                                     cls : 'roo-document-viewer-image'
29962                                 }
29963                             ]
29964                         }
29965                     ]
29966                 },
29967                 {
29968                     tag : 'div',
29969                     cls : 'roo-document-viewer-footer',
29970                     cn : {
29971                         tag : 'div',
29972                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29973                         cn : [
29974                             {
29975                                 tag : 'div',
29976                                 cls : 'btn-group roo-document-viewer-download',
29977                                 cn : [
29978                                     {
29979                                         tag : 'button',
29980                                         cls : 'btn btn-default',
29981                                         html : '<i class="fa fa-download"></i>'
29982                                     }
29983                                 ]
29984                             },
29985                             {
29986                                 tag : 'div',
29987                                 cls : 'btn-group roo-document-viewer-trash',
29988                                 cn : [
29989                                     {
29990                                         tag : 'button',
29991                                         cls : 'btn btn-default',
29992                                         html : '<i class="fa fa-trash"></i>'
29993                                     }
29994                                 ]
29995                             }
29996                         ]
29997                     }
29998                 }
29999             ]
30000         };
30001         
30002         return cfg;
30003     },
30004     
30005     initEvents : function()
30006     {
30007         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
30008         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
30009         
30010         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
30011         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
30012         
30013         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
30014         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
30015         
30016         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
30017         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
30018         
30019         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
30020         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
30021         
30022         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
30023         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
30024         
30025         this.bodyEl.on('click', this.onClick, this);
30026         this.downloadBtn.on('click', this.onDownload, this);
30027         this.trashBtn.on('click', this.onTrash, this);
30028         
30029         this.downloadBtn.hide();
30030         this.trashBtn.hide();
30031         
30032         if(this.showDownload){
30033             this.downloadBtn.show();
30034         }
30035         
30036         if(this.showTrash){
30037             this.trashBtn.show();
30038         }
30039         
30040         if(!this.showDownload && !this.showTrash) {
30041             this.footerEl.hide();
30042         }
30043         
30044     },
30045     
30046     initial : function()
30047     {
30048         this.fireEvent('initial', this);
30049         
30050     },
30051     
30052     onClick : function(e)
30053     {
30054         e.preventDefault();
30055         
30056         this.fireEvent('click', this);
30057     },
30058     
30059     onDownload : function(e)
30060     {
30061         e.preventDefault();
30062         
30063         this.fireEvent('download', this);
30064     },
30065     
30066     onTrash : function(e)
30067     {
30068         e.preventDefault();
30069         
30070         this.fireEvent('trash', this);
30071     }
30072     
30073 });
30074 /*
30075  * - LGPL
30076  *
30077  * nav progress bar
30078  * 
30079  */
30080
30081 /**
30082  * @class Roo.bootstrap.NavProgressBar
30083  * @extends Roo.bootstrap.Component
30084  * Bootstrap NavProgressBar class
30085  * 
30086  * @constructor
30087  * Create a new nav progress bar
30088  * @param {Object} config The config object
30089  */
30090
30091 Roo.bootstrap.NavProgressBar = function(config){
30092     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30093
30094     this.bullets = this.bullets || [];
30095    
30096 //    Roo.bootstrap.NavProgressBar.register(this);
30097      this.addEvents({
30098         /**
30099              * @event changed
30100              * Fires when the active item changes
30101              * @param {Roo.bootstrap.NavProgressBar} this
30102              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30103              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30104          */
30105         'changed': true
30106      });
30107     
30108 };
30109
30110 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30111     
30112     bullets : [],
30113     barItems : [],
30114     
30115     getAutoCreate : function()
30116     {
30117         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30118         
30119         cfg = {
30120             tag : 'div',
30121             cls : 'roo-navigation-bar-group',
30122             cn : [
30123                 {
30124                     tag : 'div',
30125                     cls : 'roo-navigation-top-bar'
30126                 },
30127                 {
30128                     tag : 'div',
30129                     cls : 'roo-navigation-bullets-bar',
30130                     cn : [
30131                         {
30132                             tag : 'ul',
30133                             cls : 'roo-navigation-bar'
30134                         }
30135                     ]
30136                 },
30137                 
30138                 {
30139                     tag : 'div',
30140                     cls : 'roo-navigation-bottom-bar'
30141                 }
30142             ]
30143             
30144         };
30145         
30146         return cfg;
30147         
30148     },
30149     
30150     initEvents: function() 
30151     {
30152         
30153     },
30154     
30155     onRender : function(ct, position) 
30156     {
30157         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30158         
30159         if(this.bullets.length){
30160             Roo.each(this.bullets, function(b){
30161                this.addItem(b);
30162             }, this);
30163         }
30164         
30165         this.format();
30166         
30167     },
30168     
30169     addItem : function(cfg)
30170     {
30171         var item = new Roo.bootstrap.NavProgressItem(cfg);
30172         
30173         item.parentId = this.id;
30174         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30175         
30176         if(cfg.html){
30177             var top = new Roo.bootstrap.Element({
30178                 tag : 'div',
30179                 cls : 'roo-navigation-bar-text'
30180             });
30181             
30182             var bottom = new Roo.bootstrap.Element({
30183                 tag : 'div',
30184                 cls : 'roo-navigation-bar-text'
30185             });
30186             
30187             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30188             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30189             
30190             var topText = new Roo.bootstrap.Element({
30191                 tag : 'span',
30192                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30193             });
30194             
30195             var bottomText = new Roo.bootstrap.Element({
30196                 tag : 'span',
30197                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30198             });
30199             
30200             topText.onRender(top.el, null);
30201             bottomText.onRender(bottom.el, null);
30202             
30203             item.topEl = top;
30204             item.bottomEl = bottom;
30205         }
30206         
30207         this.barItems.push(item);
30208         
30209         return item;
30210     },
30211     
30212     getActive : function()
30213     {
30214         var active = false;
30215         
30216         Roo.each(this.barItems, function(v){
30217             
30218             if (!v.isActive()) {
30219                 return;
30220             }
30221             
30222             active = v;
30223             return false;
30224             
30225         });
30226         
30227         return active;
30228     },
30229     
30230     setActiveItem : function(item)
30231     {
30232         var prev = false;
30233         
30234         Roo.each(this.barItems, function(v){
30235             if (v.rid == item.rid) {
30236                 return ;
30237             }
30238             
30239             if (v.isActive()) {
30240                 v.setActive(false);
30241                 prev = v;
30242             }
30243         });
30244
30245         item.setActive(true);
30246         
30247         this.fireEvent('changed', this, item, prev);
30248     },
30249     
30250     getBarItem: function(rid)
30251     {
30252         var ret = false;
30253         
30254         Roo.each(this.barItems, function(e) {
30255             if (e.rid != rid) {
30256                 return;
30257             }
30258             
30259             ret =  e;
30260             return false;
30261         });
30262         
30263         return ret;
30264     },
30265     
30266     indexOfItem : function(item)
30267     {
30268         var index = false;
30269         
30270         Roo.each(this.barItems, function(v, i){
30271             
30272             if (v.rid != item.rid) {
30273                 return;
30274             }
30275             
30276             index = i;
30277             return false
30278         });
30279         
30280         return index;
30281     },
30282     
30283     setActiveNext : function()
30284     {
30285         var i = this.indexOfItem(this.getActive());
30286         
30287         if (i > this.barItems.length) {
30288             return;
30289         }
30290         
30291         this.setActiveItem(this.barItems[i+1]);
30292     },
30293     
30294     setActivePrev : function()
30295     {
30296         var i = this.indexOfItem(this.getActive());
30297         
30298         if (i  < 1) {
30299             return;
30300         }
30301         
30302         this.setActiveItem(this.barItems[i-1]);
30303     },
30304     
30305     format : function()
30306     {
30307         if(!this.barItems.length){
30308             return;
30309         }
30310      
30311         var width = 100 / this.barItems.length;
30312         
30313         Roo.each(this.barItems, function(i){
30314             i.el.setStyle('width', width + '%');
30315             i.topEl.el.setStyle('width', width + '%');
30316             i.bottomEl.el.setStyle('width', width + '%');
30317         }, this);
30318         
30319     }
30320     
30321 });
30322 /*
30323  * - LGPL
30324  *
30325  * Nav Progress Item
30326  * 
30327  */
30328
30329 /**
30330  * @class Roo.bootstrap.NavProgressItem
30331  * @extends Roo.bootstrap.Component
30332  * Bootstrap NavProgressItem class
30333  * @cfg {String} rid the reference id
30334  * @cfg {Boolean} active (true|false) Is item active default false
30335  * @cfg {Boolean} disabled (true|false) Is item active default false
30336  * @cfg {String} html
30337  * @cfg {String} position (top|bottom) text position default bottom
30338  * @cfg {String} icon show icon instead of number
30339  * 
30340  * @constructor
30341  * Create a new NavProgressItem
30342  * @param {Object} config The config object
30343  */
30344 Roo.bootstrap.NavProgressItem = function(config){
30345     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30346     this.addEvents({
30347         // raw events
30348         /**
30349          * @event click
30350          * The raw click event for the entire grid.
30351          * @param {Roo.bootstrap.NavProgressItem} this
30352          * @param {Roo.EventObject} e
30353          */
30354         "click" : true
30355     });
30356    
30357 };
30358
30359 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30360     
30361     rid : '',
30362     active : false,
30363     disabled : false,
30364     html : '',
30365     position : 'bottom',
30366     icon : false,
30367     
30368     getAutoCreate : function()
30369     {
30370         var iconCls = 'roo-navigation-bar-item-icon';
30371         
30372         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30373         
30374         var cfg = {
30375             tag: 'li',
30376             cls: 'roo-navigation-bar-item',
30377             cn : [
30378                 {
30379                     tag : 'i',
30380                     cls : iconCls
30381                 }
30382             ]
30383         };
30384         
30385         if(this.active){
30386             cfg.cls += ' active';
30387         }
30388         if(this.disabled){
30389             cfg.cls += ' disabled';
30390         }
30391         
30392         return cfg;
30393     },
30394     
30395     disable : function()
30396     {
30397         this.setDisabled(true);
30398     },
30399     
30400     enable : function()
30401     {
30402         this.setDisabled(false);
30403     },
30404     
30405     initEvents: function() 
30406     {
30407         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30408         
30409         this.iconEl.on('click', this.onClick, this);
30410     },
30411     
30412     onClick : function(e)
30413     {
30414         e.preventDefault();
30415         
30416         if(this.disabled){
30417             return;
30418         }
30419         
30420         if(this.fireEvent('click', this, e) === false){
30421             return;
30422         };
30423         
30424         this.parent().setActiveItem(this);
30425     },
30426     
30427     isActive: function () 
30428     {
30429         return this.active;
30430     },
30431     
30432     setActive : function(state)
30433     {
30434         if(this.active == state){
30435             return;
30436         }
30437         
30438         this.active = state;
30439         
30440         if (state) {
30441             this.el.addClass('active');
30442             return;
30443         }
30444         
30445         this.el.removeClass('active');
30446         
30447         return;
30448     },
30449     
30450     setDisabled : function(state)
30451     {
30452         if(this.disabled == state){
30453             return;
30454         }
30455         
30456         this.disabled = state;
30457         
30458         if (state) {
30459             this.el.addClass('disabled');
30460             return;
30461         }
30462         
30463         this.el.removeClass('disabled');
30464     },
30465     
30466     tooltipEl : function()
30467     {
30468         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30469     }
30470 });
30471  
30472
30473  /*
30474  * - LGPL
30475  *
30476  * FieldLabel
30477  * 
30478  */
30479
30480 /**
30481  * @class Roo.bootstrap.FieldLabel
30482  * @extends Roo.bootstrap.Component
30483  * Bootstrap FieldLabel class
30484  * @cfg {String} html contents of the element
30485  * @cfg {String} tag tag of the element default label
30486  * @cfg {String} cls class of the element
30487  * @cfg {String} target label target 
30488  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30489  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30490  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30491  * @cfg {String} iconTooltip default "This field is required"
30492  * @cfg {String} indicatorpos (left|right) default left
30493  * 
30494  * @constructor
30495  * Create a new FieldLabel
30496  * @param {Object} config The config object
30497  */
30498
30499 Roo.bootstrap.FieldLabel = function(config){
30500     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30501     
30502     this.addEvents({
30503             /**
30504              * @event invalid
30505              * Fires after the field has been marked as invalid.
30506              * @param {Roo.form.FieldLabel} this
30507              * @param {String} msg The validation message
30508              */
30509             invalid : true,
30510             /**
30511              * @event valid
30512              * Fires after the field has been validated with no errors.
30513              * @param {Roo.form.FieldLabel} this
30514              */
30515             valid : true
30516         });
30517 };
30518
30519 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30520     
30521     tag: 'label',
30522     cls: '',
30523     html: '',
30524     target: '',
30525     allowBlank : true,
30526     invalidClass : 'has-warning',
30527     validClass : 'has-success',
30528     iconTooltip : 'This field is required',
30529     indicatorpos : 'left',
30530     
30531     getAutoCreate : function(){
30532         
30533         var cls = "";
30534         if (!this.allowBlank) {
30535             cls  = "visible";
30536         }
30537         
30538         var cfg = {
30539             tag : this.tag,
30540             cls : 'roo-bootstrap-field-label ' + this.cls,
30541             for : this.target,
30542             cn : [
30543                 {
30544                     tag : 'i',
30545                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30546                     tooltip : this.iconTooltip
30547                 },
30548                 {
30549                     tag : 'span',
30550                     html : this.html
30551                 }
30552             ] 
30553         };
30554         
30555         if(this.indicatorpos == 'right'){
30556             var cfg = {
30557                 tag : this.tag,
30558                 cls : 'roo-bootstrap-field-label ' + this.cls,
30559                 for : this.target,
30560                 cn : [
30561                     {
30562                         tag : 'span',
30563                         html : this.html
30564                     },
30565                     {
30566                         tag : 'i',
30567                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30568                         tooltip : this.iconTooltip
30569                     }
30570                 ] 
30571             };
30572         }
30573         
30574         return cfg;
30575     },
30576     
30577     initEvents: function() 
30578     {
30579         Roo.bootstrap.Element.superclass.initEvents.call(this);
30580         
30581         this.indicator = this.indicatorEl();
30582         
30583         if(this.indicator){
30584             this.indicator.removeClass('visible');
30585             this.indicator.addClass('invisible');
30586         }
30587         
30588         Roo.bootstrap.FieldLabel.register(this);
30589     },
30590     
30591     indicatorEl : function()
30592     {
30593         var indicator = this.el.select('i.roo-required-indicator',true).first();
30594         
30595         if(!indicator){
30596             return false;
30597         }
30598         
30599         return indicator;
30600         
30601     },
30602     
30603     /**
30604      * Mark this field as valid
30605      */
30606     markValid : function()
30607     {
30608         if(this.indicator){
30609             this.indicator.removeClass('visible');
30610             this.indicator.addClass('invisible');
30611         }
30612         if (Roo.bootstrap.version == 3) {
30613             this.el.removeClass(this.invalidClass);
30614             this.el.addClass(this.validClass);
30615         } else {
30616             this.el.removeClass('is-invalid');
30617             this.el.addClass('is-valid');
30618         }
30619         
30620         
30621         this.fireEvent('valid', this);
30622     },
30623     
30624     /**
30625      * Mark this field as invalid
30626      * @param {String} msg The validation message
30627      */
30628     markInvalid : function(msg)
30629     {
30630         if(this.indicator){
30631             this.indicator.removeClass('invisible');
30632             this.indicator.addClass('visible');
30633         }
30634           if (Roo.bootstrap.version == 3) {
30635             this.el.removeClass(this.validClass);
30636             this.el.addClass(this.invalidClass);
30637         } else {
30638             this.el.removeClass('is-valid');
30639             this.el.addClass('is-invalid');
30640         }
30641         
30642         
30643         this.fireEvent('invalid', this, msg);
30644     }
30645     
30646    
30647 });
30648
30649 Roo.apply(Roo.bootstrap.FieldLabel, {
30650     
30651     groups: {},
30652     
30653      /**
30654     * register a FieldLabel Group
30655     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30656     */
30657     register : function(label)
30658     {
30659         if(this.groups.hasOwnProperty(label.target)){
30660             return;
30661         }
30662      
30663         this.groups[label.target] = label;
30664         
30665     },
30666     /**
30667     * fetch a FieldLabel Group based on the target
30668     * @param {string} target
30669     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30670     */
30671     get: function(target) {
30672         if (typeof(this.groups[target]) == 'undefined') {
30673             return false;
30674         }
30675         
30676         return this.groups[target] ;
30677     }
30678 });
30679
30680  
30681
30682  /*
30683  * - LGPL
30684  *
30685  * page DateSplitField.
30686  * 
30687  */
30688
30689
30690 /**
30691  * @class Roo.bootstrap.DateSplitField
30692  * @extends Roo.bootstrap.Component
30693  * Bootstrap DateSplitField class
30694  * @cfg {string} fieldLabel - the label associated
30695  * @cfg {Number} labelWidth set the width of label (0-12)
30696  * @cfg {String} labelAlign (top|left)
30697  * @cfg {Boolean} dayAllowBlank (true|false) default false
30698  * @cfg {Boolean} monthAllowBlank (true|false) default false
30699  * @cfg {Boolean} yearAllowBlank (true|false) default false
30700  * @cfg {string} dayPlaceholder 
30701  * @cfg {string} monthPlaceholder
30702  * @cfg {string} yearPlaceholder
30703  * @cfg {string} dayFormat default 'd'
30704  * @cfg {string} monthFormat default 'm'
30705  * @cfg {string} yearFormat default 'Y'
30706  * @cfg {Number} labellg set the width of label (1-12)
30707  * @cfg {Number} labelmd set the width of label (1-12)
30708  * @cfg {Number} labelsm set the width of label (1-12)
30709  * @cfg {Number} labelxs set the width of label (1-12)
30710
30711  *     
30712  * @constructor
30713  * Create a new DateSplitField
30714  * @param {Object} config The config object
30715  */
30716
30717 Roo.bootstrap.DateSplitField = function(config){
30718     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30719     
30720     this.addEvents({
30721         // raw events
30722          /**
30723          * @event years
30724          * getting the data of years
30725          * @param {Roo.bootstrap.DateSplitField} this
30726          * @param {Object} years
30727          */
30728         "years" : true,
30729         /**
30730          * @event days
30731          * getting the data of days
30732          * @param {Roo.bootstrap.DateSplitField} this
30733          * @param {Object} days
30734          */
30735         "days" : true,
30736         /**
30737          * @event invalid
30738          * Fires after the field has been marked as invalid.
30739          * @param {Roo.form.Field} this
30740          * @param {String} msg The validation message
30741          */
30742         invalid : true,
30743        /**
30744          * @event valid
30745          * Fires after the field has been validated with no errors.
30746          * @param {Roo.form.Field} this
30747          */
30748         valid : true
30749     });
30750 };
30751
30752 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30753     
30754     fieldLabel : '',
30755     labelAlign : 'top',
30756     labelWidth : 3,
30757     dayAllowBlank : false,
30758     monthAllowBlank : false,
30759     yearAllowBlank : false,
30760     dayPlaceholder : '',
30761     monthPlaceholder : '',
30762     yearPlaceholder : '',
30763     dayFormat : 'd',
30764     monthFormat : 'm',
30765     yearFormat : 'Y',
30766     isFormField : true,
30767     labellg : 0,
30768     labelmd : 0,
30769     labelsm : 0,
30770     labelxs : 0,
30771     
30772     getAutoCreate : function()
30773     {
30774         var cfg = {
30775             tag : 'div',
30776             cls : 'row roo-date-split-field-group',
30777             cn : [
30778                 {
30779                     tag : 'input',
30780                     type : 'hidden',
30781                     cls : 'form-hidden-field roo-date-split-field-group-value',
30782                     name : this.name
30783                 }
30784             ]
30785         };
30786         
30787         var labelCls = 'col-md-12';
30788         var contentCls = 'col-md-4';
30789         
30790         if(this.fieldLabel){
30791             
30792             var label = {
30793                 tag : 'div',
30794                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30795                 cn : [
30796                     {
30797                         tag : 'label',
30798                         html : this.fieldLabel
30799                     }
30800                 ]
30801             };
30802             
30803             if(this.labelAlign == 'left'){
30804             
30805                 if(this.labelWidth > 12){
30806                     label.style = "width: " + this.labelWidth + 'px';
30807                 }
30808
30809                 if(this.labelWidth < 13 && this.labelmd == 0){
30810                     this.labelmd = this.labelWidth;
30811                 }
30812
30813                 if(this.labellg > 0){
30814                     labelCls = ' col-lg-' + this.labellg;
30815                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30816                 }
30817
30818                 if(this.labelmd > 0){
30819                     labelCls = ' col-md-' + this.labelmd;
30820                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30821                 }
30822
30823                 if(this.labelsm > 0){
30824                     labelCls = ' col-sm-' + this.labelsm;
30825                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30826                 }
30827
30828                 if(this.labelxs > 0){
30829                     labelCls = ' col-xs-' + this.labelxs;
30830                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30831                 }
30832             }
30833             
30834             label.cls += ' ' + labelCls;
30835             
30836             cfg.cn.push(label);
30837         }
30838         
30839         Roo.each(['day', 'month', 'year'], function(t){
30840             cfg.cn.push({
30841                 tag : 'div',
30842                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30843             });
30844         }, this);
30845         
30846         return cfg;
30847     },
30848     
30849     inputEl: function ()
30850     {
30851         return this.el.select('.roo-date-split-field-group-value', true).first();
30852     },
30853     
30854     onRender : function(ct, position) 
30855     {
30856         var _this = this;
30857         
30858         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30859         
30860         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30861         
30862         this.dayField = new Roo.bootstrap.ComboBox({
30863             allowBlank : this.dayAllowBlank,
30864             alwaysQuery : true,
30865             displayField : 'value',
30866             editable : false,
30867             fieldLabel : '',
30868             forceSelection : true,
30869             mode : 'local',
30870             placeholder : this.dayPlaceholder,
30871             selectOnFocus : true,
30872             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30873             triggerAction : 'all',
30874             typeAhead : true,
30875             valueField : 'value',
30876             store : new Roo.data.SimpleStore({
30877                 data : (function() {    
30878                     var days = [];
30879                     _this.fireEvent('days', _this, days);
30880                     return days;
30881                 })(),
30882                 fields : [ 'value' ]
30883             }),
30884             listeners : {
30885                 select : function (_self, record, index)
30886                 {
30887                     _this.setValue(_this.getValue());
30888                 }
30889             }
30890         });
30891
30892         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30893         
30894         this.monthField = new Roo.bootstrap.MonthField({
30895             after : '<i class=\"fa fa-calendar\"></i>',
30896             allowBlank : this.monthAllowBlank,
30897             placeholder : this.monthPlaceholder,
30898             readOnly : true,
30899             listeners : {
30900                 render : function (_self)
30901                 {
30902                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30903                         e.preventDefault();
30904                         _self.focus();
30905                     });
30906                 },
30907                 select : function (_self, oldvalue, newvalue)
30908                 {
30909                     _this.setValue(_this.getValue());
30910                 }
30911             }
30912         });
30913         
30914         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30915         
30916         this.yearField = new Roo.bootstrap.ComboBox({
30917             allowBlank : this.yearAllowBlank,
30918             alwaysQuery : true,
30919             displayField : 'value',
30920             editable : false,
30921             fieldLabel : '',
30922             forceSelection : true,
30923             mode : 'local',
30924             placeholder : this.yearPlaceholder,
30925             selectOnFocus : true,
30926             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30927             triggerAction : 'all',
30928             typeAhead : true,
30929             valueField : 'value',
30930             store : new Roo.data.SimpleStore({
30931                 data : (function() {
30932                     var years = [];
30933                     _this.fireEvent('years', _this, years);
30934                     return years;
30935                 })(),
30936                 fields : [ 'value' ]
30937             }),
30938             listeners : {
30939                 select : function (_self, record, index)
30940                 {
30941                     _this.setValue(_this.getValue());
30942                 }
30943             }
30944         });
30945
30946         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30947     },
30948     
30949     setValue : function(v, format)
30950     {
30951         this.inputEl.dom.value = v;
30952         
30953         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30954         
30955         var d = Date.parseDate(v, f);
30956         
30957         if(!d){
30958             this.validate();
30959             return;
30960         }
30961         
30962         this.setDay(d.format(this.dayFormat));
30963         this.setMonth(d.format(this.monthFormat));
30964         this.setYear(d.format(this.yearFormat));
30965         
30966         this.validate();
30967         
30968         return;
30969     },
30970     
30971     setDay : function(v)
30972     {
30973         this.dayField.setValue(v);
30974         this.inputEl.dom.value = this.getValue();
30975         this.validate();
30976         return;
30977     },
30978     
30979     setMonth : function(v)
30980     {
30981         this.monthField.setValue(v, true);
30982         this.inputEl.dom.value = this.getValue();
30983         this.validate();
30984         return;
30985     },
30986     
30987     setYear : function(v)
30988     {
30989         this.yearField.setValue(v);
30990         this.inputEl.dom.value = this.getValue();
30991         this.validate();
30992         return;
30993     },
30994     
30995     getDay : function()
30996     {
30997         return this.dayField.getValue();
30998     },
30999     
31000     getMonth : function()
31001     {
31002         return this.monthField.getValue();
31003     },
31004     
31005     getYear : function()
31006     {
31007         return this.yearField.getValue();
31008     },
31009     
31010     getValue : function()
31011     {
31012         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
31013         
31014         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
31015         
31016         return date;
31017     },
31018     
31019     reset : function()
31020     {
31021         this.setDay('');
31022         this.setMonth('');
31023         this.setYear('');
31024         this.inputEl.dom.value = '';
31025         this.validate();
31026         return;
31027     },
31028     
31029     validate : function()
31030     {
31031         var d = this.dayField.validate();
31032         var m = this.monthField.validate();
31033         var y = this.yearField.validate();
31034         
31035         var valid = true;
31036         
31037         if(
31038                 (!this.dayAllowBlank && !d) ||
31039                 (!this.monthAllowBlank && !m) ||
31040                 (!this.yearAllowBlank && !y)
31041         ){
31042             valid = false;
31043         }
31044         
31045         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31046             return valid;
31047         }
31048         
31049         if(valid){
31050             this.markValid();
31051             return valid;
31052         }
31053         
31054         this.markInvalid();
31055         
31056         return valid;
31057     },
31058     
31059     markValid : function()
31060     {
31061         
31062         var label = this.el.select('label', true).first();
31063         var icon = this.el.select('i.fa-star', true).first();
31064
31065         if(label && icon){
31066             icon.remove();
31067         }
31068         
31069         this.fireEvent('valid', this);
31070     },
31071     
31072      /**
31073      * Mark this field as invalid
31074      * @param {String} msg The validation message
31075      */
31076     markInvalid : function(msg)
31077     {
31078         
31079         var label = this.el.select('label', true).first();
31080         var icon = this.el.select('i.fa-star', true).first();
31081
31082         if(label && !icon){
31083             this.el.select('.roo-date-split-field-label', true).createChild({
31084                 tag : 'i',
31085                 cls : 'text-danger fa fa-lg fa-star',
31086                 tooltip : 'This field is required',
31087                 style : 'margin-right:5px;'
31088             }, label, true);
31089         }
31090         
31091         this.fireEvent('invalid', this, msg);
31092     },
31093     
31094     clearInvalid : function()
31095     {
31096         var label = this.el.select('label', true).first();
31097         var icon = this.el.select('i.fa-star', true).first();
31098
31099         if(label && icon){
31100             icon.remove();
31101         }
31102         
31103         this.fireEvent('valid', this);
31104     },
31105     
31106     getName: function()
31107     {
31108         return this.name;
31109     }
31110     
31111 });
31112
31113  /**
31114  *
31115  * This is based on 
31116  * http://masonry.desandro.com
31117  *
31118  * The idea is to render all the bricks based on vertical width...
31119  *
31120  * The original code extends 'outlayer' - we might need to use that....
31121  * 
31122  */
31123
31124
31125 /**
31126  * @class Roo.bootstrap.LayoutMasonry
31127  * @extends Roo.bootstrap.Component
31128  * Bootstrap Layout Masonry class
31129  * 
31130  * @constructor
31131  * Create a new Element
31132  * @param {Object} config The config object
31133  */
31134
31135 Roo.bootstrap.LayoutMasonry = function(config){
31136     
31137     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31138     
31139     this.bricks = [];
31140     
31141     Roo.bootstrap.LayoutMasonry.register(this);
31142     
31143     this.addEvents({
31144         // raw events
31145         /**
31146          * @event layout
31147          * Fire after layout the items
31148          * @param {Roo.bootstrap.LayoutMasonry} this
31149          * @param {Roo.EventObject} e
31150          */
31151         "layout" : true
31152     });
31153     
31154 };
31155
31156 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31157     
31158     /**
31159      * @cfg {Boolean} isLayoutInstant = no animation?
31160      */   
31161     isLayoutInstant : false, // needed?
31162    
31163     /**
31164      * @cfg {Number} boxWidth  width of the columns
31165      */   
31166     boxWidth : 450,
31167     
31168       /**
31169      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31170      */   
31171     boxHeight : 0,
31172     
31173     /**
31174      * @cfg {Number} padWidth padding below box..
31175      */   
31176     padWidth : 10, 
31177     
31178     /**
31179      * @cfg {Number} gutter gutter width..
31180      */   
31181     gutter : 10,
31182     
31183      /**
31184      * @cfg {Number} maxCols maximum number of columns
31185      */   
31186     
31187     maxCols: 0,
31188     
31189     /**
31190      * @cfg {Boolean} isAutoInitial defalut true
31191      */   
31192     isAutoInitial : true, 
31193     
31194     containerWidth: 0,
31195     
31196     /**
31197      * @cfg {Boolean} isHorizontal defalut false
31198      */   
31199     isHorizontal : false, 
31200
31201     currentSize : null,
31202     
31203     tag: 'div',
31204     
31205     cls: '',
31206     
31207     bricks: null, //CompositeElement
31208     
31209     cols : 1,
31210     
31211     _isLayoutInited : false,
31212     
31213 //    isAlternative : false, // only use for vertical layout...
31214     
31215     /**
31216      * @cfg {Number} alternativePadWidth padding below box..
31217      */   
31218     alternativePadWidth : 50,
31219     
31220     selectedBrick : [],
31221     
31222     getAutoCreate : function(){
31223         
31224         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31225         
31226         var cfg = {
31227             tag: this.tag,
31228             cls: 'blog-masonary-wrapper ' + this.cls,
31229             cn : {
31230                 cls : 'mas-boxes masonary'
31231             }
31232         };
31233         
31234         return cfg;
31235     },
31236     
31237     getChildContainer: function( )
31238     {
31239         if (this.boxesEl) {
31240             return this.boxesEl;
31241         }
31242         
31243         this.boxesEl = this.el.select('.mas-boxes').first();
31244         
31245         return this.boxesEl;
31246     },
31247     
31248     
31249     initEvents : function()
31250     {
31251         var _this = this;
31252         
31253         if(this.isAutoInitial){
31254             Roo.log('hook children rendered');
31255             this.on('childrenrendered', function() {
31256                 Roo.log('children rendered');
31257                 _this.initial();
31258             } ,this);
31259         }
31260     },
31261     
31262     initial : function()
31263     {
31264         this.selectedBrick = [];
31265         
31266         this.currentSize = this.el.getBox(true);
31267         
31268         Roo.EventManager.onWindowResize(this.resize, this); 
31269
31270         if(!this.isAutoInitial){
31271             this.layout();
31272             return;
31273         }
31274         
31275         this.layout();
31276         
31277         return;
31278         //this.layout.defer(500,this);
31279         
31280     },
31281     
31282     resize : function()
31283     {
31284         var cs = this.el.getBox(true);
31285         
31286         if (
31287                 this.currentSize.width == cs.width && 
31288                 this.currentSize.x == cs.x && 
31289                 this.currentSize.height == cs.height && 
31290                 this.currentSize.y == cs.y 
31291         ) {
31292             Roo.log("no change in with or X or Y");
31293             return;
31294         }
31295         
31296         this.currentSize = cs;
31297         
31298         this.layout();
31299         
31300     },
31301     
31302     layout : function()
31303     {   
31304         this._resetLayout();
31305         
31306         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31307         
31308         this.layoutItems( isInstant );
31309       
31310         this._isLayoutInited = true;
31311         
31312         this.fireEvent('layout', this);
31313         
31314     },
31315     
31316     _resetLayout : function()
31317     {
31318         if(this.isHorizontal){
31319             this.horizontalMeasureColumns();
31320             return;
31321         }
31322         
31323         this.verticalMeasureColumns();
31324         
31325     },
31326     
31327     verticalMeasureColumns : function()
31328     {
31329         this.getContainerWidth();
31330         
31331 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31332 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31333 //            return;
31334 //        }
31335         
31336         var boxWidth = this.boxWidth + this.padWidth;
31337         
31338         if(this.containerWidth < this.boxWidth){
31339             boxWidth = this.containerWidth
31340         }
31341         
31342         var containerWidth = this.containerWidth;
31343         
31344         var cols = Math.floor(containerWidth / boxWidth);
31345         
31346         this.cols = Math.max( cols, 1 );
31347         
31348         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31349         
31350         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31351         
31352         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31353         
31354         this.colWidth = boxWidth + avail - this.padWidth;
31355         
31356         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31357         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31358     },
31359     
31360     horizontalMeasureColumns : function()
31361     {
31362         this.getContainerWidth();
31363         
31364         var boxWidth = this.boxWidth;
31365         
31366         if(this.containerWidth < boxWidth){
31367             boxWidth = this.containerWidth;
31368         }
31369         
31370         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31371         
31372         this.el.setHeight(boxWidth);
31373         
31374     },
31375     
31376     getContainerWidth : function()
31377     {
31378         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31379     },
31380     
31381     layoutItems : function( isInstant )
31382     {
31383         Roo.log(this.bricks);
31384         
31385         var items = Roo.apply([], this.bricks);
31386         
31387         if(this.isHorizontal){
31388             this._horizontalLayoutItems( items , isInstant );
31389             return;
31390         }
31391         
31392 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31393 //            this._verticalAlternativeLayoutItems( items , isInstant );
31394 //            return;
31395 //        }
31396         
31397         this._verticalLayoutItems( items , isInstant );
31398         
31399     },
31400     
31401     _verticalLayoutItems : function ( items , isInstant)
31402     {
31403         if ( !items || !items.length ) {
31404             return;
31405         }
31406         
31407         var standard = [
31408             ['xs', 'xs', 'xs', 'tall'],
31409             ['xs', 'xs', 'tall'],
31410             ['xs', 'xs', 'sm'],
31411             ['xs', 'xs', 'xs'],
31412             ['xs', 'tall'],
31413             ['xs', 'sm'],
31414             ['xs', 'xs'],
31415             ['xs'],
31416             
31417             ['sm', 'xs', 'xs'],
31418             ['sm', 'xs'],
31419             ['sm'],
31420             
31421             ['tall', 'xs', 'xs', 'xs'],
31422             ['tall', 'xs', 'xs'],
31423             ['tall', 'xs'],
31424             ['tall']
31425             
31426         ];
31427         
31428         var queue = [];
31429         
31430         var boxes = [];
31431         
31432         var box = [];
31433         
31434         Roo.each(items, function(item, k){
31435             
31436             switch (item.size) {
31437                 // these layouts take up a full box,
31438                 case 'md' :
31439                 case 'md-left' :
31440                 case 'md-right' :
31441                 case 'wide' :
31442                     
31443                     if(box.length){
31444                         boxes.push(box);
31445                         box = [];
31446                     }
31447                     
31448                     boxes.push([item]);
31449                     
31450                     break;
31451                     
31452                 case 'xs' :
31453                 case 'sm' :
31454                 case 'tall' :
31455                     
31456                     box.push(item);
31457                     
31458                     break;
31459                 default :
31460                     break;
31461                     
31462             }
31463             
31464         }, this);
31465         
31466         if(box.length){
31467             boxes.push(box);
31468             box = [];
31469         }
31470         
31471         var filterPattern = function(box, length)
31472         {
31473             if(!box.length){
31474                 return;
31475             }
31476             
31477             var match = false;
31478             
31479             var pattern = box.slice(0, length);
31480             
31481             var format = [];
31482             
31483             Roo.each(pattern, function(i){
31484                 format.push(i.size);
31485             }, this);
31486             
31487             Roo.each(standard, function(s){
31488                 
31489                 if(String(s) != String(format)){
31490                     return;
31491                 }
31492                 
31493                 match = true;
31494                 return false;
31495                 
31496             }, this);
31497             
31498             if(!match && length == 1){
31499                 return;
31500             }
31501             
31502             if(!match){
31503                 filterPattern(box, length - 1);
31504                 return;
31505             }
31506                 
31507             queue.push(pattern);
31508
31509             box = box.slice(length, box.length);
31510
31511             filterPattern(box, 4);
31512
31513             return;
31514             
31515         }
31516         
31517         Roo.each(boxes, function(box, k){
31518             
31519             if(!box.length){
31520                 return;
31521             }
31522             
31523             if(box.length == 1){
31524                 queue.push(box);
31525                 return;
31526             }
31527             
31528             filterPattern(box, 4);
31529             
31530         }, this);
31531         
31532         this._processVerticalLayoutQueue( queue, isInstant );
31533         
31534     },
31535     
31536 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31537 //    {
31538 //        if ( !items || !items.length ) {
31539 //            return;
31540 //        }
31541 //
31542 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31543 //        
31544 //    },
31545     
31546     _horizontalLayoutItems : function ( items , isInstant)
31547     {
31548         if ( !items || !items.length || items.length < 3) {
31549             return;
31550         }
31551         
31552         items.reverse();
31553         
31554         var eItems = items.slice(0, 3);
31555         
31556         items = items.slice(3, items.length);
31557         
31558         var standard = [
31559             ['xs', 'xs', 'xs', 'wide'],
31560             ['xs', 'xs', 'wide'],
31561             ['xs', 'xs', 'sm'],
31562             ['xs', 'xs', 'xs'],
31563             ['xs', 'wide'],
31564             ['xs', 'sm'],
31565             ['xs', 'xs'],
31566             ['xs'],
31567             
31568             ['sm', 'xs', 'xs'],
31569             ['sm', 'xs'],
31570             ['sm'],
31571             
31572             ['wide', 'xs', 'xs', 'xs'],
31573             ['wide', 'xs', 'xs'],
31574             ['wide', 'xs'],
31575             ['wide'],
31576             
31577             ['wide-thin']
31578         ];
31579         
31580         var queue = [];
31581         
31582         var boxes = [];
31583         
31584         var box = [];
31585         
31586         Roo.each(items, function(item, k){
31587             
31588             switch (item.size) {
31589                 case 'md' :
31590                 case 'md-left' :
31591                 case 'md-right' :
31592                 case 'tall' :
31593                     
31594                     if(box.length){
31595                         boxes.push(box);
31596                         box = [];
31597                     }
31598                     
31599                     boxes.push([item]);
31600                     
31601                     break;
31602                     
31603                 case 'xs' :
31604                 case 'sm' :
31605                 case 'wide' :
31606                 case 'wide-thin' :
31607                     
31608                     box.push(item);
31609                     
31610                     break;
31611                 default :
31612                     break;
31613                     
31614             }
31615             
31616         }, this);
31617         
31618         if(box.length){
31619             boxes.push(box);
31620             box = [];
31621         }
31622         
31623         var filterPattern = function(box, length)
31624         {
31625             if(!box.length){
31626                 return;
31627             }
31628             
31629             var match = false;
31630             
31631             var pattern = box.slice(0, length);
31632             
31633             var format = [];
31634             
31635             Roo.each(pattern, function(i){
31636                 format.push(i.size);
31637             }, this);
31638             
31639             Roo.each(standard, function(s){
31640                 
31641                 if(String(s) != String(format)){
31642                     return;
31643                 }
31644                 
31645                 match = true;
31646                 return false;
31647                 
31648             }, this);
31649             
31650             if(!match && length == 1){
31651                 return;
31652             }
31653             
31654             if(!match){
31655                 filterPattern(box, length - 1);
31656                 return;
31657             }
31658                 
31659             queue.push(pattern);
31660
31661             box = box.slice(length, box.length);
31662
31663             filterPattern(box, 4);
31664
31665             return;
31666             
31667         }
31668         
31669         Roo.each(boxes, function(box, k){
31670             
31671             if(!box.length){
31672                 return;
31673             }
31674             
31675             if(box.length == 1){
31676                 queue.push(box);
31677                 return;
31678             }
31679             
31680             filterPattern(box, 4);
31681             
31682         }, this);
31683         
31684         
31685         var prune = [];
31686         
31687         var pos = this.el.getBox(true);
31688         
31689         var minX = pos.x;
31690         
31691         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31692         
31693         var hit_end = false;
31694         
31695         Roo.each(queue, function(box){
31696             
31697             if(hit_end){
31698                 
31699                 Roo.each(box, function(b){
31700                 
31701                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31702                     b.el.hide();
31703
31704                 }, this);
31705
31706                 return;
31707             }
31708             
31709             var mx = 0;
31710             
31711             Roo.each(box, function(b){
31712                 
31713                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31714                 b.el.show();
31715
31716                 mx = Math.max(mx, b.x);
31717                 
31718             }, this);
31719             
31720             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31721             
31722             if(maxX < minX){
31723                 
31724                 Roo.each(box, function(b){
31725                 
31726                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31727                     b.el.hide();
31728                     
31729                 }, this);
31730                 
31731                 hit_end = true;
31732                 
31733                 return;
31734             }
31735             
31736             prune.push(box);
31737             
31738         }, this);
31739         
31740         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31741     },
31742     
31743     /** Sets position of item in DOM
31744     * @param {Element} item
31745     * @param {Number} x - horizontal position
31746     * @param {Number} y - vertical position
31747     * @param {Boolean} isInstant - disables transitions
31748     */
31749     _processVerticalLayoutQueue : function( queue, isInstant )
31750     {
31751         var pos = this.el.getBox(true);
31752         var x = pos.x;
31753         var y = pos.y;
31754         var maxY = [];
31755         
31756         for (var i = 0; i < this.cols; i++){
31757             maxY[i] = pos.y;
31758         }
31759         
31760         Roo.each(queue, function(box, k){
31761             
31762             var col = k % this.cols;
31763             
31764             Roo.each(box, function(b,kk){
31765                 
31766                 b.el.position('absolute');
31767                 
31768                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31769                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31770                 
31771                 if(b.size == 'md-left' || b.size == 'md-right'){
31772                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31773                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31774                 }
31775                 
31776                 b.el.setWidth(width);
31777                 b.el.setHeight(height);
31778                 // iframe?
31779                 b.el.select('iframe',true).setSize(width,height);
31780                 
31781             }, this);
31782             
31783             for (var i = 0; i < this.cols; i++){
31784                 
31785                 if(maxY[i] < maxY[col]){
31786                     col = i;
31787                     continue;
31788                 }
31789                 
31790                 col = Math.min(col, i);
31791                 
31792             }
31793             
31794             x = pos.x + col * (this.colWidth + this.padWidth);
31795             
31796             y = maxY[col];
31797             
31798             var positions = [];
31799             
31800             switch (box.length){
31801                 case 1 :
31802                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31803                     break;
31804                 case 2 :
31805                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31806                     break;
31807                 case 3 :
31808                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31809                     break;
31810                 case 4 :
31811                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31812                     break;
31813                 default :
31814                     break;
31815             }
31816             
31817             Roo.each(box, function(b,kk){
31818                 
31819                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31820                 
31821                 var sz = b.el.getSize();
31822                 
31823                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31824                 
31825             }, this);
31826             
31827         }, this);
31828         
31829         var mY = 0;
31830         
31831         for (var i = 0; i < this.cols; i++){
31832             mY = Math.max(mY, maxY[i]);
31833         }
31834         
31835         this.el.setHeight(mY - pos.y);
31836         
31837     },
31838     
31839 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31840 //    {
31841 //        var pos = this.el.getBox(true);
31842 //        var x = pos.x;
31843 //        var y = pos.y;
31844 //        var maxX = pos.right;
31845 //        
31846 //        var maxHeight = 0;
31847 //        
31848 //        Roo.each(items, function(item, k){
31849 //            
31850 //            var c = k % 2;
31851 //            
31852 //            item.el.position('absolute');
31853 //                
31854 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31855 //
31856 //            item.el.setWidth(width);
31857 //
31858 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31859 //
31860 //            item.el.setHeight(height);
31861 //            
31862 //            if(c == 0){
31863 //                item.el.setXY([x, y], isInstant ? false : true);
31864 //            } else {
31865 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31866 //            }
31867 //            
31868 //            y = y + height + this.alternativePadWidth;
31869 //            
31870 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31871 //            
31872 //        }, this);
31873 //        
31874 //        this.el.setHeight(maxHeight);
31875 //        
31876 //    },
31877     
31878     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31879     {
31880         var pos = this.el.getBox(true);
31881         
31882         var minX = pos.x;
31883         var minY = pos.y;
31884         
31885         var maxX = pos.right;
31886         
31887         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31888         
31889         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31890         
31891         Roo.each(queue, function(box, k){
31892             
31893             Roo.each(box, function(b, kk){
31894                 
31895                 b.el.position('absolute');
31896                 
31897                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31898                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31899                 
31900                 if(b.size == 'md-left' || b.size == 'md-right'){
31901                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31902                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31903                 }
31904                 
31905                 b.el.setWidth(width);
31906                 b.el.setHeight(height);
31907                 
31908             }, this);
31909             
31910             if(!box.length){
31911                 return;
31912             }
31913             
31914             var positions = [];
31915             
31916             switch (box.length){
31917                 case 1 :
31918                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31919                     break;
31920                 case 2 :
31921                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31922                     break;
31923                 case 3 :
31924                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31925                     break;
31926                 case 4 :
31927                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31928                     break;
31929                 default :
31930                     break;
31931             }
31932             
31933             Roo.each(box, function(b,kk){
31934                 
31935                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31936                 
31937                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31938                 
31939             }, this);
31940             
31941         }, this);
31942         
31943     },
31944     
31945     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31946     {
31947         Roo.each(eItems, function(b,k){
31948             
31949             b.size = (k == 0) ? 'sm' : 'xs';
31950             b.x = (k == 0) ? 2 : 1;
31951             b.y = (k == 0) ? 2 : 1;
31952             
31953             b.el.position('absolute');
31954             
31955             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31956                 
31957             b.el.setWidth(width);
31958             
31959             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31960             
31961             b.el.setHeight(height);
31962             
31963         }, this);
31964
31965         var positions = [];
31966         
31967         positions.push({
31968             x : maxX - this.unitWidth * 2 - this.gutter,
31969             y : minY
31970         });
31971         
31972         positions.push({
31973             x : maxX - this.unitWidth,
31974             y : minY + (this.unitWidth + this.gutter) * 2
31975         });
31976         
31977         positions.push({
31978             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31979             y : minY
31980         });
31981         
31982         Roo.each(eItems, function(b,k){
31983             
31984             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31985
31986         }, this);
31987         
31988     },
31989     
31990     getVerticalOneBoxColPositions : function(x, y, box)
31991     {
31992         var pos = [];
31993         
31994         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31995         
31996         if(box[0].size == 'md-left'){
31997             rand = 0;
31998         }
31999         
32000         if(box[0].size == 'md-right'){
32001             rand = 1;
32002         }
32003         
32004         pos.push({
32005             x : x + (this.unitWidth + this.gutter) * rand,
32006             y : y
32007         });
32008         
32009         return pos;
32010     },
32011     
32012     getVerticalTwoBoxColPositions : function(x, y, box)
32013     {
32014         var pos = [];
32015         
32016         if(box[0].size == 'xs'){
32017             
32018             pos.push({
32019                 x : x,
32020                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
32021             });
32022
32023             pos.push({
32024                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
32025                 y : y
32026             });
32027             
32028             return pos;
32029             
32030         }
32031         
32032         pos.push({
32033             x : x,
32034             y : y
32035         });
32036
32037         pos.push({
32038             x : x + (this.unitWidth + this.gutter) * 2,
32039             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32040         });
32041         
32042         return pos;
32043         
32044     },
32045     
32046     getVerticalThreeBoxColPositions : function(x, y, box)
32047     {
32048         var pos = [];
32049         
32050         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32051             
32052             pos.push({
32053                 x : x,
32054                 y : y
32055             });
32056
32057             pos.push({
32058                 x : x + (this.unitWidth + this.gutter) * 1,
32059                 y : y
32060             });
32061             
32062             pos.push({
32063                 x : x + (this.unitWidth + this.gutter) * 2,
32064                 y : y
32065             });
32066             
32067             return pos;
32068             
32069         }
32070         
32071         if(box[0].size == 'xs' && box[1].size == 'xs'){
32072             
32073             pos.push({
32074                 x : x,
32075                 y : y
32076             });
32077
32078             pos.push({
32079                 x : x,
32080                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32081             });
32082             
32083             pos.push({
32084                 x : x + (this.unitWidth + this.gutter) * 1,
32085                 y : y
32086             });
32087             
32088             return pos;
32089             
32090         }
32091         
32092         pos.push({
32093             x : x,
32094             y : y
32095         });
32096
32097         pos.push({
32098             x : x + (this.unitWidth + this.gutter) * 2,
32099             y : y
32100         });
32101
32102         pos.push({
32103             x : x + (this.unitWidth + this.gutter) * 2,
32104             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32105         });
32106             
32107         return pos;
32108         
32109     },
32110     
32111     getVerticalFourBoxColPositions : function(x, y, box)
32112     {
32113         var pos = [];
32114         
32115         if(box[0].size == 'xs'){
32116             
32117             pos.push({
32118                 x : x,
32119                 y : y
32120             });
32121
32122             pos.push({
32123                 x : x,
32124                 y : y + (this.unitHeight + this.gutter) * 1
32125             });
32126             
32127             pos.push({
32128                 x : x,
32129                 y : y + (this.unitHeight + this.gutter) * 2
32130             });
32131             
32132             pos.push({
32133                 x : x + (this.unitWidth + this.gutter) * 1,
32134                 y : y
32135             });
32136             
32137             return pos;
32138             
32139         }
32140         
32141         pos.push({
32142             x : x,
32143             y : y
32144         });
32145
32146         pos.push({
32147             x : x + (this.unitWidth + this.gutter) * 2,
32148             y : y
32149         });
32150
32151         pos.push({
32152             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32153             y : y + (this.unitHeight + this.gutter) * 1
32154         });
32155
32156         pos.push({
32157             x : x + (this.unitWidth + this.gutter) * 2,
32158             y : y + (this.unitWidth + this.gutter) * 2
32159         });
32160
32161         return pos;
32162         
32163     },
32164     
32165     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32166     {
32167         var pos = [];
32168         
32169         if(box[0].size == 'md-left'){
32170             pos.push({
32171                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32172                 y : minY
32173             });
32174             
32175             return pos;
32176         }
32177         
32178         if(box[0].size == 'md-right'){
32179             pos.push({
32180                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32181                 y : minY + (this.unitWidth + this.gutter) * 1
32182             });
32183             
32184             return pos;
32185         }
32186         
32187         var rand = Math.floor(Math.random() * (4 - box[0].y));
32188         
32189         pos.push({
32190             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32191             y : minY + (this.unitWidth + this.gutter) * rand
32192         });
32193         
32194         return pos;
32195         
32196     },
32197     
32198     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32199     {
32200         var pos = [];
32201         
32202         if(box[0].size == 'xs'){
32203             
32204             pos.push({
32205                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32206                 y : minY
32207             });
32208
32209             pos.push({
32210                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32211                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32212             });
32213             
32214             return pos;
32215             
32216         }
32217         
32218         pos.push({
32219             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32220             y : minY
32221         });
32222
32223         pos.push({
32224             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32225             y : minY + (this.unitWidth + this.gutter) * 2
32226         });
32227         
32228         return pos;
32229         
32230     },
32231     
32232     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32233     {
32234         var pos = [];
32235         
32236         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32237             
32238             pos.push({
32239                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32240                 y : minY
32241             });
32242
32243             pos.push({
32244                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32245                 y : minY + (this.unitWidth + this.gutter) * 1
32246             });
32247             
32248             pos.push({
32249                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32250                 y : minY + (this.unitWidth + this.gutter) * 2
32251             });
32252             
32253             return pos;
32254             
32255         }
32256         
32257         if(box[0].size == 'xs' && box[1].size == 'xs'){
32258             
32259             pos.push({
32260                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32261                 y : minY
32262             });
32263
32264             pos.push({
32265                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32266                 y : minY
32267             });
32268             
32269             pos.push({
32270                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32271                 y : minY + (this.unitWidth + this.gutter) * 1
32272             });
32273             
32274             return pos;
32275             
32276         }
32277         
32278         pos.push({
32279             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32280             y : minY
32281         });
32282
32283         pos.push({
32284             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32285             y : minY + (this.unitWidth + this.gutter) * 2
32286         });
32287
32288         pos.push({
32289             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32290             y : minY + (this.unitWidth + this.gutter) * 2
32291         });
32292             
32293         return pos;
32294         
32295     },
32296     
32297     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32298     {
32299         var pos = [];
32300         
32301         if(box[0].size == 'xs'){
32302             
32303             pos.push({
32304                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32305                 y : minY
32306             });
32307
32308             pos.push({
32309                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32310                 y : minY
32311             });
32312             
32313             pos.push({
32314                 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),
32315                 y : minY
32316             });
32317             
32318             pos.push({
32319                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32320                 y : minY + (this.unitWidth + this.gutter) * 1
32321             });
32322             
32323             return pos;
32324             
32325         }
32326         
32327         pos.push({
32328             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32329             y : minY
32330         });
32331         
32332         pos.push({
32333             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32334             y : minY + (this.unitWidth + this.gutter) * 2
32335         });
32336         
32337         pos.push({
32338             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32339             y : minY + (this.unitWidth + this.gutter) * 2
32340         });
32341         
32342         pos.push({
32343             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),
32344             y : minY + (this.unitWidth + this.gutter) * 2
32345         });
32346
32347         return pos;
32348         
32349     },
32350     
32351     /**
32352     * remove a Masonry Brick
32353     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32354     */
32355     removeBrick : function(brick_id)
32356     {
32357         if (!brick_id) {
32358             return;
32359         }
32360         
32361         for (var i = 0; i<this.bricks.length; i++) {
32362             if (this.bricks[i].id == brick_id) {
32363                 this.bricks.splice(i,1);
32364                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32365                 this.initial();
32366             }
32367         }
32368     },
32369     
32370     /**
32371     * adds a Masonry Brick
32372     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32373     */
32374     addBrick : function(cfg)
32375     {
32376         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32377         //this.register(cn);
32378         cn.parentId = this.id;
32379         cn.render(this.el);
32380         return cn;
32381     },
32382     
32383     /**
32384     * register a Masonry Brick
32385     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32386     */
32387     
32388     register : function(brick)
32389     {
32390         this.bricks.push(brick);
32391         brick.masonryId = this.id;
32392     },
32393     
32394     /**
32395     * clear all the Masonry Brick
32396     */
32397     clearAll : function()
32398     {
32399         this.bricks = [];
32400         //this.getChildContainer().dom.innerHTML = "";
32401         this.el.dom.innerHTML = '';
32402     },
32403     
32404     getSelected : function()
32405     {
32406         if (!this.selectedBrick) {
32407             return false;
32408         }
32409         
32410         return this.selectedBrick;
32411     }
32412 });
32413
32414 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32415     
32416     groups: {},
32417      /**
32418     * register a Masonry Layout
32419     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32420     */
32421     
32422     register : function(layout)
32423     {
32424         this.groups[layout.id] = layout;
32425     },
32426     /**
32427     * fetch a  Masonry Layout based on the masonry layout ID
32428     * @param {string} the masonry layout to add
32429     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32430     */
32431     
32432     get: function(layout_id) {
32433         if (typeof(this.groups[layout_id]) == 'undefined') {
32434             return false;
32435         }
32436         return this.groups[layout_id] ;
32437     }
32438     
32439     
32440     
32441 });
32442
32443  
32444
32445  /**
32446  *
32447  * This is based on 
32448  * http://masonry.desandro.com
32449  *
32450  * The idea is to render all the bricks based on vertical width...
32451  *
32452  * The original code extends 'outlayer' - we might need to use that....
32453  * 
32454  */
32455
32456
32457 /**
32458  * @class Roo.bootstrap.LayoutMasonryAuto
32459  * @extends Roo.bootstrap.Component
32460  * Bootstrap Layout Masonry class
32461  * 
32462  * @constructor
32463  * Create a new Element
32464  * @param {Object} config The config object
32465  */
32466
32467 Roo.bootstrap.LayoutMasonryAuto = function(config){
32468     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32469 };
32470
32471 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32472     
32473       /**
32474      * @cfg {Boolean} isFitWidth  - resize the width..
32475      */   
32476     isFitWidth : false,  // options..
32477     /**
32478      * @cfg {Boolean} isOriginLeft = left align?
32479      */   
32480     isOriginLeft : true,
32481     /**
32482      * @cfg {Boolean} isOriginTop = top align?
32483      */   
32484     isOriginTop : false,
32485     /**
32486      * @cfg {Boolean} isLayoutInstant = no animation?
32487      */   
32488     isLayoutInstant : false, // needed?
32489     /**
32490      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32491      */   
32492     isResizingContainer : true,
32493     /**
32494      * @cfg {Number} columnWidth  width of the columns 
32495      */   
32496     
32497     columnWidth : 0,
32498     
32499     /**
32500      * @cfg {Number} maxCols maximum number of columns
32501      */   
32502     
32503     maxCols: 0,
32504     /**
32505      * @cfg {Number} padHeight padding below box..
32506      */   
32507     
32508     padHeight : 10, 
32509     
32510     /**
32511      * @cfg {Boolean} isAutoInitial defalut true
32512      */   
32513     
32514     isAutoInitial : true, 
32515     
32516     // private?
32517     gutter : 0,
32518     
32519     containerWidth: 0,
32520     initialColumnWidth : 0,
32521     currentSize : null,
32522     
32523     colYs : null, // array.
32524     maxY : 0,
32525     padWidth: 10,
32526     
32527     
32528     tag: 'div',
32529     cls: '',
32530     bricks: null, //CompositeElement
32531     cols : 0, // array?
32532     // element : null, // wrapped now this.el
32533     _isLayoutInited : null, 
32534     
32535     
32536     getAutoCreate : function(){
32537         
32538         var cfg = {
32539             tag: this.tag,
32540             cls: 'blog-masonary-wrapper ' + this.cls,
32541             cn : {
32542                 cls : 'mas-boxes masonary'
32543             }
32544         };
32545         
32546         return cfg;
32547     },
32548     
32549     getChildContainer: function( )
32550     {
32551         if (this.boxesEl) {
32552             return this.boxesEl;
32553         }
32554         
32555         this.boxesEl = this.el.select('.mas-boxes').first();
32556         
32557         return this.boxesEl;
32558     },
32559     
32560     
32561     initEvents : function()
32562     {
32563         var _this = this;
32564         
32565         if(this.isAutoInitial){
32566             Roo.log('hook children rendered');
32567             this.on('childrenrendered', function() {
32568                 Roo.log('children rendered');
32569                 _this.initial();
32570             } ,this);
32571         }
32572         
32573     },
32574     
32575     initial : function()
32576     {
32577         this.reloadItems();
32578
32579         this.currentSize = this.el.getBox(true);
32580
32581         /// was window resize... - let's see if this works..
32582         Roo.EventManager.onWindowResize(this.resize, this); 
32583
32584         if(!this.isAutoInitial){
32585             this.layout();
32586             return;
32587         }
32588         
32589         this.layout.defer(500,this);
32590     },
32591     
32592     reloadItems: function()
32593     {
32594         this.bricks = this.el.select('.masonry-brick', true);
32595         
32596         this.bricks.each(function(b) {
32597             //Roo.log(b.getSize());
32598             if (!b.attr('originalwidth')) {
32599                 b.attr('originalwidth',  b.getSize().width);
32600             }
32601             
32602         });
32603         
32604         Roo.log(this.bricks.elements.length);
32605     },
32606     
32607     resize : function()
32608     {
32609         Roo.log('resize');
32610         var cs = this.el.getBox(true);
32611         
32612         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32613             Roo.log("no change in with or X");
32614             return;
32615         }
32616         this.currentSize = cs;
32617         this.layout();
32618     },
32619     
32620     layout : function()
32621     {
32622          Roo.log('layout');
32623         this._resetLayout();
32624         //this._manageStamps();
32625       
32626         // don't animate first layout
32627         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32628         this.layoutItems( isInstant );
32629       
32630         // flag for initalized
32631         this._isLayoutInited = true;
32632     },
32633     
32634     layoutItems : function( isInstant )
32635     {
32636         //var items = this._getItemsForLayout( this.items );
32637         // original code supports filtering layout items.. we just ignore it..
32638         
32639         this._layoutItems( this.bricks , isInstant );
32640       
32641         this._postLayout();
32642     },
32643     _layoutItems : function ( items , isInstant)
32644     {
32645        //this.fireEvent( 'layout', this, items );
32646     
32647
32648         if ( !items || !items.elements.length ) {
32649           // no items, emit event with empty array
32650             return;
32651         }
32652
32653         var queue = [];
32654         items.each(function(item) {
32655             Roo.log("layout item");
32656             Roo.log(item);
32657             // get x/y object from method
32658             var position = this._getItemLayoutPosition( item );
32659             // enqueue
32660             position.item = item;
32661             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32662             queue.push( position );
32663         }, this);
32664       
32665         this._processLayoutQueue( queue );
32666     },
32667     /** Sets position of item in DOM
32668     * @param {Element} item
32669     * @param {Number} x - horizontal position
32670     * @param {Number} y - vertical position
32671     * @param {Boolean} isInstant - disables transitions
32672     */
32673     _processLayoutQueue : function( queue )
32674     {
32675         for ( var i=0, len = queue.length; i < len; i++ ) {
32676             var obj = queue[i];
32677             obj.item.position('absolute');
32678             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32679         }
32680     },
32681       
32682     
32683     /**
32684     * Any logic you want to do after each layout,
32685     * i.e. size the container
32686     */
32687     _postLayout : function()
32688     {
32689         this.resizeContainer();
32690     },
32691     
32692     resizeContainer : function()
32693     {
32694         if ( !this.isResizingContainer ) {
32695             return;
32696         }
32697         var size = this._getContainerSize();
32698         if ( size ) {
32699             this.el.setSize(size.width,size.height);
32700             this.boxesEl.setSize(size.width,size.height);
32701         }
32702     },
32703     
32704     
32705     
32706     _resetLayout : function()
32707     {
32708         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32709         this.colWidth = this.el.getWidth();
32710         //this.gutter = this.el.getWidth(); 
32711         
32712         this.measureColumns();
32713
32714         // reset column Y
32715         var i = this.cols;
32716         this.colYs = [];
32717         while (i--) {
32718             this.colYs.push( 0 );
32719         }
32720     
32721         this.maxY = 0;
32722     },
32723
32724     measureColumns : function()
32725     {
32726         this.getContainerWidth();
32727       // if columnWidth is 0, default to outerWidth of first item
32728         if ( !this.columnWidth ) {
32729             var firstItem = this.bricks.first();
32730             Roo.log(firstItem);
32731             this.columnWidth  = this.containerWidth;
32732             if (firstItem && firstItem.attr('originalwidth') ) {
32733                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32734             }
32735             // columnWidth fall back to item of first element
32736             Roo.log("set column width?");
32737                         this.initialColumnWidth = this.columnWidth  ;
32738
32739             // if first elem has no width, default to size of container
32740             
32741         }
32742         
32743         
32744         if (this.initialColumnWidth) {
32745             this.columnWidth = this.initialColumnWidth;
32746         }
32747         
32748         
32749             
32750         // column width is fixed at the top - however if container width get's smaller we should
32751         // reduce it...
32752         
32753         // this bit calcs how man columns..
32754             
32755         var columnWidth = this.columnWidth += this.gutter;
32756       
32757         // calculate columns
32758         var containerWidth = this.containerWidth + this.gutter;
32759         
32760         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32761         // fix rounding errors, typically with gutters
32762         var excess = columnWidth - containerWidth % columnWidth;
32763         
32764         
32765         // if overshoot is less than a pixel, round up, otherwise floor it
32766         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32767         cols = Math[ mathMethod ]( cols );
32768         this.cols = Math.max( cols, 1 );
32769         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32770         
32771          // padding positioning..
32772         var totalColWidth = this.cols * this.columnWidth;
32773         var padavail = this.containerWidth - totalColWidth;
32774         // so for 2 columns - we need 3 'pads'
32775         
32776         var padNeeded = (1+this.cols) * this.padWidth;
32777         
32778         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32779         
32780         this.columnWidth += padExtra
32781         //this.padWidth = Math.floor(padavail /  ( this.cols));
32782         
32783         // adjust colum width so that padding is fixed??
32784         
32785         // we have 3 columns ... total = width * 3
32786         // we have X left over... that should be used by 
32787         
32788         //if (this.expandC) {
32789             
32790         //}
32791         
32792         
32793         
32794     },
32795     
32796     getContainerWidth : function()
32797     {
32798        /* // container is parent if fit width
32799         var container = this.isFitWidth ? this.element.parentNode : this.element;
32800         // check that this.size and size are there
32801         // IE8 triggers resize on body size change, so they might not be
32802         
32803         var size = getSize( container );  //FIXME
32804         this.containerWidth = size && size.innerWidth; //FIXME
32805         */
32806          
32807         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32808         
32809     },
32810     
32811     _getItemLayoutPosition : function( item )  // what is item?
32812     {
32813         // we resize the item to our columnWidth..
32814       
32815         item.setWidth(this.columnWidth);
32816         item.autoBoxAdjust  = false;
32817         
32818         var sz = item.getSize();
32819  
32820         // how many columns does this brick span
32821         var remainder = this.containerWidth % this.columnWidth;
32822         
32823         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32824         // round if off by 1 pixel, otherwise use ceil
32825         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32826         colSpan = Math.min( colSpan, this.cols );
32827         
32828         // normally this should be '1' as we dont' currently allow multi width columns..
32829         
32830         var colGroup = this._getColGroup( colSpan );
32831         // get the minimum Y value from the columns
32832         var minimumY = Math.min.apply( Math, colGroup );
32833         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32834         
32835         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32836          
32837         // position the brick
32838         var position = {
32839             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32840             y: this.currentSize.y + minimumY + this.padHeight
32841         };
32842         
32843         Roo.log(position);
32844         // apply setHeight to necessary columns
32845         var setHeight = minimumY + sz.height + this.padHeight;
32846         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32847         
32848         var setSpan = this.cols + 1 - colGroup.length;
32849         for ( var i = 0; i < setSpan; i++ ) {
32850           this.colYs[ shortColIndex + i ] = setHeight ;
32851         }
32852       
32853         return position;
32854     },
32855     
32856     /**
32857      * @param {Number} colSpan - number of columns the element spans
32858      * @returns {Array} colGroup
32859      */
32860     _getColGroup : function( colSpan )
32861     {
32862         if ( colSpan < 2 ) {
32863           // if brick spans only one column, use all the column Ys
32864           return this.colYs;
32865         }
32866       
32867         var colGroup = [];
32868         // how many different places could this brick fit horizontally
32869         var groupCount = this.cols + 1 - colSpan;
32870         // for each group potential horizontal position
32871         for ( var i = 0; i < groupCount; i++ ) {
32872           // make an array of colY values for that one group
32873           var groupColYs = this.colYs.slice( i, i + colSpan );
32874           // and get the max value of the array
32875           colGroup[i] = Math.max.apply( Math, groupColYs );
32876         }
32877         return colGroup;
32878     },
32879     /*
32880     _manageStamp : function( stamp )
32881     {
32882         var stampSize =  stamp.getSize();
32883         var offset = stamp.getBox();
32884         // get the columns that this stamp affects
32885         var firstX = this.isOriginLeft ? offset.x : offset.right;
32886         var lastX = firstX + stampSize.width;
32887         var firstCol = Math.floor( firstX / this.columnWidth );
32888         firstCol = Math.max( 0, firstCol );
32889         
32890         var lastCol = Math.floor( lastX / this.columnWidth );
32891         // lastCol should not go over if multiple of columnWidth #425
32892         lastCol -= lastX % this.columnWidth ? 0 : 1;
32893         lastCol = Math.min( this.cols - 1, lastCol );
32894         
32895         // set colYs to bottom of the stamp
32896         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32897             stampSize.height;
32898             
32899         for ( var i = firstCol; i <= lastCol; i++ ) {
32900           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32901         }
32902     },
32903     */
32904     
32905     _getContainerSize : function()
32906     {
32907         this.maxY = Math.max.apply( Math, this.colYs );
32908         var size = {
32909             height: this.maxY
32910         };
32911       
32912         if ( this.isFitWidth ) {
32913             size.width = this._getContainerFitWidth();
32914         }
32915       
32916         return size;
32917     },
32918     
32919     _getContainerFitWidth : function()
32920     {
32921         var unusedCols = 0;
32922         // count unused columns
32923         var i = this.cols;
32924         while ( --i ) {
32925           if ( this.colYs[i] !== 0 ) {
32926             break;
32927           }
32928           unusedCols++;
32929         }
32930         // fit container to columns that have been used
32931         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32932     },
32933     
32934     needsResizeLayout : function()
32935     {
32936         var previousWidth = this.containerWidth;
32937         this.getContainerWidth();
32938         return previousWidth !== this.containerWidth;
32939     }
32940  
32941 });
32942
32943  
32944
32945  /*
32946  * - LGPL
32947  *
32948  * element
32949  * 
32950  */
32951
32952 /**
32953  * @class Roo.bootstrap.MasonryBrick
32954  * @extends Roo.bootstrap.Component
32955  * Bootstrap MasonryBrick class
32956  * 
32957  * @constructor
32958  * Create a new MasonryBrick
32959  * @param {Object} config The config object
32960  */
32961
32962 Roo.bootstrap.MasonryBrick = function(config){
32963     
32964     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32965     
32966     Roo.bootstrap.MasonryBrick.register(this);
32967     
32968     this.addEvents({
32969         // raw events
32970         /**
32971          * @event click
32972          * When a MasonryBrick is clcik
32973          * @param {Roo.bootstrap.MasonryBrick} this
32974          * @param {Roo.EventObject} e
32975          */
32976         "click" : true
32977     });
32978 };
32979
32980 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32981     
32982     /**
32983      * @cfg {String} title
32984      */   
32985     title : '',
32986     /**
32987      * @cfg {String} html
32988      */   
32989     html : '',
32990     /**
32991      * @cfg {String} bgimage
32992      */   
32993     bgimage : '',
32994     /**
32995      * @cfg {String} videourl
32996      */   
32997     videourl : '',
32998     /**
32999      * @cfg {String} cls
33000      */   
33001     cls : '',
33002     /**
33003      * @cfg {String} href
33004      */   
33005     href : '',
33006     /**
33007      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
33008      */   
33009     size : 'xs',
33010     
33011     /**
33012      * @cfg {String} placetitle (center|bottom)
33013      */   
33014     placetitle : '',
33015     
33016     /**
33017      * @cfg {Boolean} isFitContainer defalut true
33018      */   
33019     isFitContainer : true, 
33020     
33021     /**
33022      * @cfg {Boolean} preventDefault defalut false
33023      */   
33024     preventDefault : false, 
33025     
33026     /**
33027      * @cfg {Boolean} inverse defalut false
33028      */   
33029     maskInverse : false, 
33030     
33031     getAutoCreate : function()
33032     {
33033         if(!this.isFitContainer){
33034             return this.getSplitAutoCreate();
33035         }
33036         
33037         var cls = 'masonry-brick masonry-brick-full';
33038         
33039         if(this.href.length){
33040             cls += ' masonry-brick-link';
33041         }
33042         
33043         if(this.bgimage.length){
33044             cls += ' masonry-brick-image';
33045         }
33046         
33047         if(this.maskInverse){
33048             cls += ' mask-inverse';
33049         }
33050         
33051         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33052             cls += ' enable-mask';
33053         }
33054         
33055         if(this.size){
33056             cls += ' masonry-' + this.size + '-brick';
33057         }
33058         
33059         if(this.placetitle.length){
33060             
33061             switch (this.placetitle) {
33062                 case 'center' :
33063                     cls += ' masonry-center-title';
33064                     break;
33065                 case 'bottom' :
33066                     cls += ' masonry-bottom-title';
33067                     break;
33068                 default:
33069                     break;
33070             }
33071             
33072         } else {
33073             if(!this.html.length && !this.bgimage.length){
33074                 cls += ' masonry-center-title';
33075             }
33076
33077             if(!this.html.length && this.bgimage.length){
33078                 cls += ' masonry-bottom-title';
33079             }
33080         }
33081         
33082         if(this.cls){
33083             cls += ' ' + this.cls;
33084         }
33085         
33086         var cfg = {
33087             tag: (this.href.length) ? 'a' : 'div',
33088             cls: cls,
33089             cn: [
33090                 {
33091                     tag: 'div',
33092                     cls: 'masonry-brick-mask'
33093                 },
33094                 {
33095                     tag: 'div',
33096                     cls: 'masonry-brick-paragraph',
33097                     cn: []
33098                 }
33099             ]
33100         };
33101         
33102         if(this.href.length){
33103             cfg.href = this.href;
33104         }
33105         
33106         var cn = cfg.cn[1].cn;
33107         
33108         if(this.title.length){
33109             cn.push({
33110                 tag: 'h4',
33111                 cls: 'masonry-brick-title',
33112                 html: this.title
33113             });
33114         }
33115         
33116         if(this.html.length){
33117             cn.push({
33118                 tag: 'p',
33119                 cls: 'masonry-brick-text',
33120                 html: this.html
33121             });
33122         }
33123         
33124         if (!this.title.length && !this.html.length) {
33125             cfg.cn[1].cls += ' hide';
33126         }
33127         
33128         if(this.bgimage.length){
33129             cfg.cn.push({
33130                 tag: 'img',
33131                 cls: 'masonry-brick-image-view',
33132                 src: this.bgimage
33133             });
33134         }
33135         
33136         if(this.videourl.length){
33137             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33138             // youtube support only?
33139             cfg.cn.push({
33140                 tag: 'iframe',
33141                 cls: 'masonry-brick-image-view',
33142                 src: vurl,
33143                 frameborder : 0,
33144                 allowfullscreen : true
33145             });
33146         }
33147         
33148         return cfg;
33149         
33150     },
33151     
33152     getSplitAutoCreate : function()
33153     {
33154         var cls = 'masonry-brick masonry-brick-split';
33155         
33156         if(this.href.length){
33157             cls += ' masonry-brick-link';
33158         }
33159         
33160         if(this.bgimage.length){
33161             cls += ' masonry-brick-image';
33162         }
33163         
33164         if(this.size){
33165             cls += ' masonry-' + this.size + '-brick';
33166         }
33167         
33168         switch (this.placetitle) {
33169             case 'center' :
33170                 cls += ' masonry-center-title';
33171                 break;
33172             case 'bottom' :
33173                 cls += ' masonry-bottom-title';
33174                 break;
33175             default:
33176                 if(!this.bgimage.length){
33177                     cls += ' masonry-center-title';
33178                 }
33179
33180                 if(this.bgimage.length){
33181                     cls += ' masonry-bottom-title';
33182                 }
33183                 break;
33184         }
33185         
33186         if(this.cls){
33187             cls += ' ' + this.cls;
33188         }
33189         
33190         var cfg = {
33191             tag: (this.href.length) ? 'a' : 'div',
33192             cls: cls,
33193             cn: [
33194                 {
33195                     tag: 'div',
33196                     cls: 'masonry-brick-split-head',
33197                     cn: [
33198                         {
33199                             tag: 'div',
33200                             cls: 'masonry-brick-paragraph',
33201                             cn: []
33202                         }
33203                     ]
33204                 },
33205                 {
33206                     tag: 'div',
33207                     cls: 'masonry-brick-split-body',
33208                     cn: []
33209                 }
33210             ]
33211         };
33212         
33213         if(this.href.length){
33214             cfg.href = this.href;
33215         }
33216         
33217         if(this.title.length){
33218             cfg.cn[0].cn[0].cn.push({
33219                 tag: 'h4',
33220                 cls: 'masonry-brick-title',
33221                 html: this.title
33222             });
33223         }
33224         
33225         if(this.html.length){
33226             cfg.cn[1].cn.push({
33227                 tag: 'p',
33228                 cls: 'masonry-brick-text',
33229                 html: this.html
33230             });
33231         }
33232
33233         if(this.bgimage.length){
33234             cfg.cn[0].cn.push({
33235                 tag: 'img',
33236                 cls: 'masonry-brick-image-view',
33237                 src: this.bgimage
33238             });
33239         }
33240         
33241         if(this.videourl.length){
33242             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33243             // youtube support only?
33244             cfg.cn[0].cn.cn.push({
33245                 tag: 'iframe',
33246                 cls: 'masonry-brick-image-view',
33247                 src: vurl,
33248                 frameborder : 0,
33249                 allowfullscreen : true
33250             });
33251         }
33252         
33253         return cfg;
33254     },
33255     
33256     initEvents: function() 
33257     {
33258         switch (this.size) {
33259             case 'xs' :
33260                 this.x = 1;
33261                 this.y = 1;
33262                 break;
33263             case 'sm' :
33264                 this.x = 2;
33265                 this.y = 2;
33266                 break;
33267             case 'md' :
33268             case 'md-left' :
33269             case 'md-right' :
33270                 this.x = 3;
33271                 this.y = 3;
33272                 break;
33273             case 'tall' :
33274                 this.x = 2;
33275                 this.y = 3;
33276                 break;
33277             case 'wide' :
33278                 this.x = 3;
33279                 this.y = 2;
33280                 break;
33281             case 'wide-thin' :
33282                 this.x = 3;
33283                 this.y = 1;
33284                 break;
33285                         
33286             default :
33287                 break;
33288         }
33289         
33290         if(Roo.isTouch){
33291             this.el.on('touchstart', this.onTouchStart, this);
33292             this.el.on('touchmove', this.onTouchMove, this);
33293             this.el.on('touchend', this.onTouchEnd, this);
33294             this.el.on('contextmenu', this.onContextMenu, this);
33295         } else {
33296             this.el.on('mouseenter'  ,this.enter, this);
33297             this.el.on('mouseleave', this.leave, this);
33298             this.el.on('click', this.onClick, this);
33299         }
33300         
33301         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33302             this.parent().bricks.push(this);   
33303         }
33304         
33305     },
33306     
33307     onClick: function(e, el)
33308     {
33309         var time = this.endTimer - this.startTimer;
33310         // Roo.log(e.preventDefault());
33311         if(Roo.isTouch){
33312             if(time > 1000){
33313                 e.preventDefault();
33314                 return;
33315             }
33316         }
33317         
33318         if(!this.preventDefault){
33319             return;
33320         }
33321         
33322         e.preventDefault();
33323         
33324         if (this.activeClass != '') {
33325             this.selectBrick();
33326         }
33327         
33328         this.fireEvent('click', this, e);
33329     },
33330     
33331     enter: function(e, el)
33332     {
33333         e.preventDefault();
33334         
33335         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33336             return;
33337         }
33338         
33339         if(this.bgimage.length && this.html.length){
33340             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33341         }
33342     },
33343     
33344     leave: function(e, el)
33345     {
33346         e.preventDefault();
33347         
33348         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33349             return;
33350         }
33351         
33352         if(this.bgimage.length && this.html.length){
33353             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33354         }
33355     },
33356     
33357     onTouchStart: function(e, el)
33358     {
33359 //        e.preventDefault();
33360         
33361         this.touchmoved = false;
33362         
33363         if(!this.isFitContainer){
33364             return;
33365         }
33366         
33367         if(!this.bgimage.length || !this.html.length){
33368             return;
33369         }
33370         
33371         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33372         
33373         this.timer = new Date().getTime();
33374         
33375     },
33376     
33377     onTouchMove: function(e, el)
33378     {
33379         this.touchmoved = true;
33380     },
33381     
33382     onContextMenu : function(e,el)
33383     {
33384         e.preventDefault();
33385         e.stopPropagation();
33386         return false;
33387     },
33388     
33389     onTouchEnd: function(e, el)
33390     {
33391 //        e.preventDefault();
33392         
33393         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33394         
33395             this.leave(e,el);
33396             
33397             return;
33398         }
33399         
33400         if(!this.bgimage.length || !this.html.length){
33401             
33402             if(this.href.length){
33403                 window.location.href = this.href;
33404             }
33405             
33406             return;
33407         }
33408         
33409         if(!this.isFitContainer){
33410             return;
33411         }
33412         
33413         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33414         
33415         window.location.href = this.href;
33416     },
33417     
33418     //selection on single brick only
33419     selectBrick : function() {
33420         
33421         if (!this.parentId) {
33422             return;
33423         }
33424         
33425         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33426         var index = m.selectedBrick.indexOf(this.id);
33427         
33428         if ( index > -1) {
33429             m.selectedBrick.splice(index,1);
33430             this.el.removeClass(this.activeClass);
33431             return;
33432         }
33433         
33434         for(var i = 0; i < m.selectedBrick.length; i++) {
33435             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33436             b.el.removeClass(b.activeClass);
33437         }
33438         
33439         m.selectedBrick = [];
33440         
33441         m.selectedBrick.push(this.id);
33442         this.el.addClass(this.activeClass);
33443         return;
33444     },
33445     
33446     isSelected : function(){
33447         return this.el.hasClass(this.activeClass);
33448         
33449     }
33450 });
33451
33452 Roo.apply(Roo.bootstrap.MasonryBrick, {
33453     
33454     //groups: {},
33455     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33456      /**
33457     * register a Masonry Brick
33458     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33459     */
33460     
33461     register : function(brick)
33462     {
33463         //this.groups[brick.id] = brick;
33464         this.groups.add(brick.id, brick);
33465     },
33466     /**
33467     * fetch a  masonry brick based on the masonry brick ID
33468     * @param {string} the masonry brick to add
33469     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33470     */
33471     
33472     get: function(brick_id) 
33473     {
33474         // if (typeof(this.groups[brick_id]) == 'undefined') {
33475         //     return false;
33476         // }
33477         // return this.groups[brick_id] ;
33478         
33479         if(this.groups.key(brick_id)) {
33480             return this.groups.key(brick_id);
33481         }
33482         
33483         return false;
33484     }
33485     
33486     
33487     
33488 });
33489
33490  /*
33491  * - LGPL
33492  *
33493  * element
33494  * 
33495  */
33496
33497 /**
33498  * @class Roo.bootstrap.Brick
33499  * @extends Roo.bootstrap.Component
33500  * Bootstrap Brick class
33501  * 
33502  * @constructor
33503  * Create a new Brick
33504  * @param {Object} config The config object
33505  */
33506
33507 Roo.bootstrap.Brick = function(config){
33508     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33509     
33510     this.addEvents({
33511         // raw events
33512         /**
33513          * @event click
33514          * When a Brick is click
33515          * @param {Roo.bootstrap.Brick} this
33516          * @param {Roo.EventObject} e
33517          */
33518         "click" : true
33519     });
33520 };
33521
33522 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33523     
33524     /**
33525      * @cfg {String} title
33526      */   
33527     title : '',
33528     /**
33529      * @cfg {String} html
33530      */   
33531     html : '',
33532     /**
33533      * @cfg {String} bgimage
33534      */   
33535     bgimage : '',
33536     /**
33537      * @cfg {String} cls
33538      */   
33539     cls : '',
33540     /**
33541      * @cfg {String} href
33542      */   
33543     href : '',
33544     /**
33545      * @cfg {String} video
33546      */   
33547     video : '',
33548     /**
33549      * @cfg {Boolean} square
33550      */   
33551     square : true,
33552     
33553     getAutoCreate : function()
33554     {
33555         var cls = 'roo-brick';
33556         
33557         if(this.href.length){
33558             cls += ' roo-brick-link';
33559         }
33560         
33561         if(this.bgimage.length){
33562             cls += ' roo-brick-image';
33563         }
33564         
33565         if(!this.html.length && !this.bgimage.length){
33566             cls += ' roo-brick-center-title';
33567         }
33568         
33569         if(!this.html.length && this.bgimage.length){
33570             cls += ' roo-brick-bottom-title';
33571         }
33572         
33573         if(this.cls){
33574             cls += ' ' + this.cls;
33575         }
33576         
33577         var cfg = {
33578             tag: (this.href.length) ? 'a' : 'div',
33579             cls: cls,
33580             cn: [
33581                 {
33582                     tag: 'div',
33583                     cls: 'roo-brick-paragraph',
33584                     cn: []
33585                 }
33586             ]
33587         };
33588         
33589         if(this.href.length){
33590             cfg.href = this.href;
33591         }
33592         
33593         var cn = cfg.cn[0].cn;
33594         
33595         if(this.title.length){
33596             cn.push({
33597                 tag: 'h4',
33598                 cls: 'roo-brick-title',
33599                 html: this.title
33600             });
33601         }
33602         
33603         if(this.html.length){
33604             cn.push({
33605                 tag: 'p',
33606                 cls: 'roo-brick-text',
33607                 html: this.html
33608             });
33609         } else {
33610             cn.cls += ' hide';
33611         }
33612         
33613         if(this.bgimage.length){
33614             cfg.cn.push({
33615                 tag: 'img',
33616                 cls: 'roo-brick-image-view',
33617                 src: this.bgimage
33618             });
33619         }
33620         
33621         return cfg;
33622     },
33623     
33624     initEvents: function() 
33625     {
33626         if(this.title.length || this.html.length){
33627             this.el.on('mouseenter'  ,this.enter, this);
33628             this.el.on('mouseleave', this.leave, this);
33629         }
33630         
33631         Roo.EventManager.onWindowResize(this.resize, this); 
33632         
33633         if(this.bgimage.length){
33634             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33635             this.imageEl.on('load', this.onImageLoad, this);
33636             return;
33637         }
33638         
33639         this.resize();
33640     },
33641     
33642     onImageLoad : function()
33643     {
33644         this.resize();
33645     },
33646     
33647     resize : function()
33648     {
33649         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33650         
33651         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33652         
33653         if(this.bgimage.length){
33654             var image = this.el.select('.roo-brick-image-view', true).first();
33655             
33656             image.setWidth(paragraph.getWidth());
33657             
33658             if(this.square){
33659                 image.setHeight(paragraph.getWidth());
33660             }
33661             
33662             this.el.setHeight(image.getHeight());
33663             paragraph.setHeight(image.getHeight());
33664             
33665         }
33666         
33667     },
33668     
33669     enter: function(e, el)
33670     {
33671         e.preventDefault();
33672         
33673         if(this.bgimage.length){
33674             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33675             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33676         }
33677     },
33678     
33679     leave: function(e, el)
33680     {
33681         e.preventDefault();
33682         
33683         if(this.bgimage.length){
33684             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33685             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33686         }
33687     }
33688     
33689 });
33690
33691  
33692
33693  /*
33694  * - LGPL
33695  *
33696  * Number field 
33697  */
33698
33699 /**
33700  * @class Roo.bootstrap.NumberField
33701  * @extends Roo.bootstrap.Input
33702  * Bootstrap NumberField class
33703  * 
33704  * 
33705  * 
33706  * 
33707  * @constructor
33708  * Create a new NumberField
33709  * @param {Object} config The config object
33710  */
33711
33712 Roo.bootstrap.NumberField = function(config){
33713     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33714 };
33715
33716 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33717     
33718     /**
33719      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33720      */
33721     allowDecimals : true,
33722     /**
33723      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33724      */
33725     decimalSeparator : ".",
33726     /**
33727      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33728      */
33729     decimalPrecision : 2,
33730     /**
33731      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33732      */
33733     allowNegative : true,
33734     
33735     /**
33736      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33737      */
33738     allowZero: true,
33739     /**
33740      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33741      */
33742     minValue : Number.NEGATIVE_INFINITY,
33743     /**
33744      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33745      */
33746     maxValue : Number.MAX_VALUE,
33747     /**
33748      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33749      */
33750     minText : "The minimum value for this field is {0}",
33751     /**
33752      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33753      */
33754     maxText : "The maximum value for this field is {0}",
33755     /**
33756      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33757      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33758      */
33759     nanText : "{0} is not a valid number",
33760     /**
33761      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33762      */
33763     thousandsDelimiter : false,
33764     /**
33765      * @cfg {String} valueAlign alignment of value
33766      */
33767     valueAlign : "left",
33768
33769     getAutoCreate : function()
33770     {
33771         var hiddenInput = {
33772             tag: 'input',
33773             type: 'hidden',
33774             id: Roo.id(),
33775             cls: 'hidden-number-input'
33776         };
33777         
33778         if (this.name) {
33779             hiddenInput.name = this.name;
33780         }
33781         
33782         this.name = '';
33783         
33784         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33785         
33786         this.name = hiddenInput.name;
33787         
33788         if(cfg.cn.length > 0) {
33789             cfg.cn.push(hiddenInput);
33790         }
33791         
33792         return cfg;
33793     },
33794
33795     // private
33796     initEvents : function()
33797     {   
33798         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33799         
33800         var allowed = "0123456789";
33801         
33802         if(this.allowDecimals){
33803             allowed += this.decimalSeparator;
33804         }
33805         
33806         if(this.allowNegative){
33807             allowed += "-";
33808         }
33809         
33810         if(this.thousandsDelimiter) {
33811             allowed += ",";
33812         }
33813         
33814         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33815         
33816         var keyPress = function(e){
33817             
33818             var k = e.getKey();
33819             
33820             var c = e.getCharCode();
33821             
33822             if(
33823                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33824                     allowed.indexOf(String.fromCharCode(c)) === -1
33825             ){
33826                 e.stopEvent();
33827                 return;
33828             }
33829             
33830             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33831                 return;
33832             }
33833             
33834             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33835                 e.stopEvent();
33836             }
33837         };
33838         
33839         this.el.on("keypress", keyPress, this);
33840     },
33841     
33842     validateValue : function(value)
33843     {
33844         
33845         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33846             return false;
33847         }
33848         
33849         var num = this.parseValue(value);
33850         
33851         if(isNaN(num)){
33852             this.markInvalid(String.format(this.nanText, value));
33853             return false;
33854         }
33855         
33856         if(num < this.minValue){
33857             this.markInvalid(String.format(this.minText, this.minValue));
33858             return false;
33859         }
33860         
33861         if(num > this.maxValue){
33862             this.markInvalid(String.format(this.maxText, this.maxValue));
33863             return false;
33864         }
33865         
33866         return true;
33867     },
33868
33869     getValue : function()
33870     {
33871         var v = this.hiddenEl().getValue();
33872         
33873         return this.fixPrecision(this.parseValue(v));
33874     },
33875
33876     parseValue : function(value)
33877     {
33878         if(this.thousandsDelimiter) {
33879             value += "";
33880             r = new RegExp(",", "g");
33881             value = value.replace(r, "");
33882         }
33883         
33884         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33885         return isNaN(value) ? '' : value;
33886     },
33887
33888     fixPrecision : function(value)
33889     {
33890         if(this.thousandsDelimiter) {
33891             value += "";
33892             r = new RegExp(",", "g");
33893             value = value.replace(r, "");
33894         }
33895         
33896         var nan = isNaN(value);
33897         
33898         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33899             return nan ? '' : value;
33900         }
33901         return parseFloat(value).toFixed(this.decimalPrecision);
33902     },
33903
33904     setValue : function(v)
33905     {
33906         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33907         
33908         this.value = v;
33909         
33910         if(this.rendered){
33911             
33912             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33913             
33914             this.inputEl().dom.value = (v == '') ? '' :
33915                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33916             
33917             if(!this.allowZero && v === '0') {
33918                 this.hiddenEl().dom.value = '';
33919                 this.inputEl().dom.value = '';
33920             }
33921             
33922             this.validate();
33923         }
33924     },
33925
33926     decimalPrecisionFcn : function(v)
33927     {
33928         return Math.floor(v);
33929     },
33930
33931     beforeBlur : function()
33932     {
33933         var v = this.parseValue(this.getRawValue());
33934         
33935         if(v || v === 0 || v === ''){
33936             this.setValue(v);
33937         }
33938     },
33939     
33940     hiddenEl : function()
33941     {
33942         return this.el.select('input.hidden-number-input',true).first();
33943     }
33944     
33945 });
33946
33947  
33948
33949 /*
33950 * Licence: LGPL
33951 */
33952
33953 /**
33954  * @class Roo.bootstrap.DocumentSlider
33955  * @extends Roo.bootstrap.Component
33956  * Bootstrap DocumentSlider class
33957  * 
33958  * @constructor
33959  * Create a new DocumentViewer
33960  * @param {Object} config The config object
33961  */
33962
33963 Roo.bootstrap.DocumentSlider = function(config){
33964     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33965     
33966     this.files = [];
33967     
33968     this.addEvents({
33969         /**
33970          * @event initial
33971          * Fire after initEvent
33972          * @param {Roo.bootstrap.DocumentSlider} this
33973          */
33974         "initial" : true,
33975         /**
33976          * @event update
33977          * Fire after update
33978          * @param {Roo.bootstrap.DocumentSlider} this
33979          */
33980         "update" : true,
33981         /**
33982          * @event click
33983          * Fire after click
33984          * @param {Roo.bootstrap.DocumentSlider} this
33985          */
33986         "click" : true
33987     });
33988 };
33989
33990 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33991     
33992     files : false,
33993     
33994     indicator : 0,
33995     
33996     getAutoCreate : function()
33997     {
33998         var cfg = {
33999             tag : 'div',
34000             cls : 'roo-document-slider',
34001             cn : [
34002                 {
34003                     tag : 'div',
34004                     cls : 'roo-document-slider-header',
34005                     cn : [
34006                         {
34007                             tag : 'div',
34008                             cls : 'roo-document-slider-header-title'
34009                         }
34010                     ]
34011                 },
34012                 {
34013                     tag : 'div',
34014                     cls : 'roo-document-slider-body',
34015                     cn : [
34016                         {
34017                             tag : 'div',
34018                             cls : 'roo-document-slider-prev',
34019                             cn : [
34020                                 {
34021                                     tag : 'i',
34022                                     cls : 'fa fa-chevron-left'
34023                                 }
34024                             ]
34025                         },
34026                         {
34027                             tag : 'div',
34028                             cls : 'roo-document-slider-thumb',
34029                             cn : [
34030                                 {
34031                                     tag : 'img',
34032                                     cls : 'roo-document-slider-image'
34033                                 }
34034                             ]
34035                         },
34036                         {
34037                             tag : 'div',
34038                             cls : 'roo-document-slider-next',
34039                             cn : [
34040                                 {
34041                                     tag : 'i',
34042                                     cls : 'fa fa-chevron-right'
34043                                 }
34044                             ]
34045                         }
34046                     ]
34047                 }
34048             ]
34049         };
34050         
34051         return cfg;
34052     },
34053     
34054     initEvents : function()
34055     {
34056         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34057         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34058         
34059         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34060         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34061         
34062         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34063         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34064         
34065         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34066         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34067         
34068         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34069         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34070         
34071         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34072         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34073         
34074         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34075         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34076         
34077         this.thumbEl.on('click', this.onClick, this);
34078         
34079         this.prevIndicator.on('click', this.prev, this);
34080         
34081         this.nextIndicator.on('click', this.next, this);
34082         
34083     },
34084     
34085     initial : function()
34086     {
34087         if(this.files.length){
34088             this.indicator = 1;
34089             this.update()
34090         }
34091         
34092         this.fireEvent('initial', this);
34093     },
34094     
34095     update : function()
34096     {
34097         this.imageEl.attr('src', this.files[this.indicator - 1]);
34098         
34099         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34100         
34101         this.prevIndicator.show();
34102         
34103         if(this.indicator == 1){
34104             this.prevIndicator.hide();
34105         }
34106         
34107         this.nextIndicator.show();
34108         
34109         if(this.indicator == this.files.length){
34110             this.nextIndicator.hide();
34111         }
34112         
34113         this.thumbEl.scrollTo('top');
34114         
34115         this.fireEvent('update', this);
34116     },
34117     
34118     onClick : function(e)
34119     {
34120         e.preventDefault();
34121         
34122         this.fireEvent('click', this);
34123     },
34124     
34125     prev : function(e)
34126     {
34127         e.preventDefault();
34128         
34129         this.indicator = Math.max(1, this.indicator - 1);
34130         
34131         this.update();
34132     },
34133     
34134     next : function(e)
34135     {
34136         e.preventDefault();
34137         
34138         this.indicator = Math.min(this.files.length, this.indicator + 1);
34139         
34140         this.update();
34141     }
34142 });
34143 /*
34144  * - LGPL
34145  *
34146  * RadioSet
34147  *
34148  *
34149  */
34150
34151 /**
34152  * @class Roo.bootstrap.RadioSet
34153  * @extends Roo.bootstrap.Input
34154  * Bootstrap RadioSet class
34155  * @cfg {String} indicatorpos (left|right) default left
34156  * @cfg {Boolean} inline (true|false) inline the element (default true)
34157  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34158  * @constructor
34159  * Create a new RadioSet
34160  * @param {Object} config The config object
34161  */
34162
34163 Roo.bootstrap.RadioSet = function(config){
34164     
34165     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34166     
34167     this.radioes = [];
34168     
34169     Roo.bootstrap.RadioSet.register(this);
34170     
34171     this.addEvents({
34172         /**
34173         * @event check
34174         * Fires when the element is checked or unchecked.
34175         * @param {Roo.bootstrap.RadioSet} this This radio
34176         * @param {Roo.bootstrap.Radio} item The checked item
34177         */
34178        check : true,
34179        /**
34180         * @event click
34181         * Fires when the element is click.
34182         * @param {Roo.bootstrap.RadioSet} this This radio set
34183         * @param {Roo.bootstrap.Radio} item The checked item
34184         * @param {Roo.EventObject} e The event object
34185         */
34186        click : true
34187     });
34188     
34189 };
34190
34191 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34192
34193     radioes : false,
34194     
34195     inline : true,
34196     
34197     weight : '',
34198     
34199     indicatorpos : 'left',
34200     
34201     getAutoCreate : function()
34202     {
34203         var label = {
34204             tag : 'label',
34205             cls : 'roo-radio-set-label',
34206             cn : [
34207                 {
34208                     tag : 'span',
34209                     html : this.fieldLabel
34210                 }
34211             ]
34212         };
34213         if (Roo.bootstrap.version == 3) {
34214             
34215             
34216             if(this.indicatorpos == 'left'){
34217                 label.cn.unshift({
34218                     tag : 'i',
34219                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34220                     tooltip : 'This field is required'
34221                 });
34222             } else {
34223                 label.cn.push({
34224                     tag : 'i',
34225                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34226                     tooltip : 'This field is required'
34227                 });
34228             }
34229         }
34230         var items = {
34231             tag : 'div',
34232             cls : 'roo-radio-set-items'
34233         };
34234         
34235         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34236         
34237         if (align === 'left' && this.fieldLabel.length) {
34238             
34239             items = {
34240                 cls : "roo-radio-set-right", 
34241                 cn: [
34242                     items
34243                 ]
34244             };
34245             
34246             if(this.labelWidth > 12){
34247                 label.style = "width: " + this.labelWidth + 'px';
34248             }
34249             
34250             if(this.labelWidth < 13 && this.labelmd == 0){
34251                 this.labelmd = this.labelWidth;
34252             }
34253             
34254             if(this.labellg > 0){
34255                 label.cls += ' col-lg-' + this.labellg;
34256                 items.cls += ' col-lg-' + (12 - this.labellg);
34257             }
34258             
34259             if(this.labelmd > 0){
34260                 label.cls += ' col-md-' + this.labelmd;
34261                 items.cls += ' col-md-' + (12 - this.labelmd);
34262             }
34263             
34264             if(this.labelsm > 0){
34265                 label.cls += ' col-sm-' + this.labelsm;
34266                 items.cls += ' col-sm-' + (12 - this.labelsm);
34267             }
34268             
34269             if(this.labelxs > 0){
34270                 label.cls += ' col-xs-' + this.labelxs;
34271                 items.cls += ' col-xs-' + (12 - this.labelxs);
34272             }
34273         }
34274         
34275         var cfg = {
34276             tag : 'div',
34277             cls : 'roo-radio-set',
34278             cn : [
34279                 {
34280                     tag : 'input',
34281                     cls : 'roo-radio-set-input',
34282                     type : 'hidden',
34283                     name : this.name,
34284                     value : this.value ? this.value :  ''
34285                 },
34286                 label,
34287                 items
34288             ]
34289         };
34290         
34291         if(this.weight.length){
34292             cfg.cls += ' roo-radio-' + this.weight;
34293         }
34294         
34295         if(this.inline) {
34296             cfg.cls += ' roo-radio-set-inline';
34297         }
34298         
34299         var settings=this;
34300         ['xs','sm','md','lg'].map(function(size){
34301             if (settings[size]) {
34302                 cfg.cls += ' col-' + size + '-' + settings[size];
34303             }
34304         });
34305         
34306         return cfg;
34307         
34308     },
34309
34310     initEvents : function()
34311     {
34312         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34313         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34314         
34315         if(!this.fieldLabel.length){
34316             this.labelEl.hide();
34317         }
34318         
34319         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34320         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34321         
34322         this.indicator = this.indicatorEl();
34323         
34324         if(this.indicator){
34325             this.indicator.addClass('invisible');
34326         }
34327         
34328         this.originalValue = this.getValue();
34329         
34330     },
34331     
34332     inputEl: function ()
34333     {
34334         return this.el.select('.roo-radio-set-input', true).first();
34335     },
34336     
34337     getChildContainer : function()
34338     {
34339         return this.itemsEl;
34340     },
34341     
34342     register : function(item)
34343     {
34344         this.radioes.push(item);
34345         
34346     },
34347     
34348     validate : function()
34349     {   
34350         if(this.getVisibilityEl().hasClass('hidden')){
34351             return true;
34352         }
34353         
34354         var valid = false;
34355         
34356         Roo.each(this.radioes, function(i){
34357             if(!i.checked){
34358                 return;
34359             }
34360             
34361             valid = true;
34362             return false;
34363         });
34364         
34365         if(this.allowBlank) {
34366             return true;
34367         }
34368         
34369         if(this.disabled || valid){
34370             this.markValid();
34371             return true;
34372         }
34373         
34374         this.markInvalid();
34375         return false;
34376         
34377     },
34378     
34379     markValid : function()
34380     {
34381         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34382             this.indicatorEl().removeClass('visible');
34383             this.indicatorEl().addClass('invisible');
34384         }
34385         
34386         
34387         if (Roo.bootstrap.version == 3) {
34388             this.el.removeClass([this.invalidClass, this.validClass]);
34389             this.el.addClass(this.validClass);
34390         } else {
34391             this.el.removeClass(['is-invalid','is-valid']);
34392             this.el.addClass(['is-valid']);
34393         }
34394         this.fireEvent('valid', this);
34395     },
34396     
34397     markInvalid : function(msg)
34398     {
34399         if(this.allowBlank || this.disabled){
34400             return;
34401         }
34402         
34403         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34404             this.indicatorEl().removeClass('invisible');
34405             this.indicatorEl().addClass('visible');
34406         }
34407         if (Roo.bootstrap.version == 3) {
34408             this.el.removeClass([this.invalidClass, this.validClass]);
34409             this.el.addClass(this.invalidClass);
34410         } else {
34411             this.el.removeClass(['is-invalid','is-valid']);
34412             this.el.addClass(['is-invalid']);
34413         }
34414         
34415         this.fireEvent('invalid', this, msg);
34416         
34417     },
34418     
34419     setValue : function(v, suppressEvent)
34420     {   
34421         if(this.value === v){
34422             return;
34423         }
34424         
34425         this.value = v;
34426         
34427         if(this.rendered){
34428             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34429         }
34430         
34431         Roo.each(this.radioes, function(i){
34432             i.checked = false;
34433             i.el.removeClass('checked');
34434         });
34435         
34436         Roo.each(this.radioes, function(i){
34437             
34438             if(i.value === v || i.value.toString() === v.toString()){
34439                 i.checked = true;
34440                 i.el.addClass('checked');
34441                 
34442                 if(suppressEvent !== true){
34443                     this.fireEvent('check', this, i);
34444                 }
34445                 
34446                 return false;
34447             }
34448             
34449         }, this);
34450         
34451         this.validate();
34452     },
34453     
34454     clearInvalid : function(){
34455         
34456         if(!this.el || this.preventMark){
34457             return;
34458         }
34459         
34460         this.el.removeClass([this.invalidClass]);
34461         
34462         this.fireEvent('valid', this);
34463     }
34464     
34465 });
34466
34467 Roo.apply(Roo.bootstrap.RadioSet, {
34468     
34469     groups: {},
34470     
34471     register : function(set)
34472     {
34473         this.groups[set.name] = set;
34474     },
34475     
34476     get: function(name) 
34477     {
34478         if (typeof(this.groups[name]) == 'undefined') {
34479             return false;
34480         }
34481         
34482         return this.groups[name] ;
34483     }
34484     
34485 });
34486 /*
34487  * Based on:
34488  * Ext JS Library 1.1.1
34489  * Copyright(c) 2006-2007, Ext JS, LLC.
34490  *
34491  * Originally Released Under LGPL - original licence link has changed is not relivant.
34492  *
34493  * Fork - LGPL
34494  * <script type="text/javascript">
34495  */
34496
34497
34498 /**
34499  * @class Roo.bootstrap.SplitBar
34500  * @extends Roo.util.Observable
34501  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34502  * <br><br>
34503  * Usage:
34504  * <pre><code>
34505 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34506                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34507 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34508 split.minSize = 100;
34509 split.maxSize = 600;
34510 split.animate = true;
34511 split.on('moved', splitterMoved);
34512 </code></pre>
34513  * @constructor
34514  * Create a new SplitBar
34515  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34516  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34517  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34518  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34519                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34520                         position of the SplitBar).
34521  */
34522 Roo.bootstrap.SplitBar = function(cfg){
34523     
34524     /** @private */
34525     
34526     //{
34527     //  dragElement : elm
34528     //  resizingElement: el,
34529         // optional..
34530     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34531     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34532         // existingProxy ???
34533     //}
34534     
34535     this.el = Roo.get(cfg.dragElement, true);
34536     this.el.dom.unselectable = "on";
34537     /** @private */
34538     this.resizingEl = Roo.get(cfg.resizingElement, true);
34539
34540     /**
34541      * @private
34542      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34543      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34544      * @type Number
34545      */
34546     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34547     
34548     /**
34549      * The minimum size of the resizing element. (Defaults to 0)
34550      * @type Number
34551      */
34552     this.minSize = 0;
34553     
34554     /**
34555      * The maximum size of the resizing element. (Defaults to 2000)
34556      * @type Number
34557      */
34558     this.maxSize = 2000;
34559     
34560     /**
34561      * Whether to animate the transition to the new size
34562      * @type Boolean
34563      */
34564     this.animate = false;
34565     
34566     /**
34567      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34568      * @type Boolean
34569      */
34570     this.useShim = false;
34571     
34572     /** @private */
34573     this.shim = null;
34574     
34575     if(!cfg.existingProxy){
34576         /** @private */
34577         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34578     }else{
34579         this.proxy = Roo.get(cfg.existingProxy).dom;
34580     }
34581     /** @private */
34582     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34583     
34584     /** @private */
34585     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34586     
34587     /** @private */
34588     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34589     
34590     /** @private */
34591     this.dragSpecs = {};
34592     
34593     /**
34594      * @private The adapter to use to positon and resize elements
34595      */
34596     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34597     this.adapter.init(this);
34598     
34599     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34600         /** @private */
34601         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34602         this.el.addClass("roo-splitbar-h");
34603     }else{
34604         /** @private */
34605         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34606         this.el.addClass("roo-splitbar-v");
34607     }
34608     
34609     this.addEvents({
34610         /**
34611          * @event resize
34612          * Fires when the splitter is moved (alias for {@link #event-moved})
34613          * @param {Roo.bootstrap.SplitBar} this
34614          * @param {Number} newSize the new width or height
34615          */
34616         "resize" : true,
34617         /**
34618          * @event moved
34619          * Fires when the splitter is moved
34620          * @param {Roo.bootstrap.SplitBar} this
34621          * @param {Number} newSize the new width or height
34622          */
34623         "moved" : true,
34624         /**
34625          * @event beforeresize
34626          * Fires before the splitter is dragged
34627          * @param {Roo.bootstrap.SplitBar} this
34628          */
34629         "beforeresize" : true,
34630
34631         "beforeapply" : true
34632     });
34633
34634     Roo.util.Observable.call(this);
34635 };
34636
34637 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34638     onStartProxyDrag : function(x, y){
34639         this.fireEvent("beforeresize", this);
34640         if(!this.overlay){
34641             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34642             o.unselectable();
34643             o.enableDisplayMode("block");
34644             // all splitbars share the same overlay
34645             Roo.bootstrap.SplitBar.prototype.overlay = o;
34646         }
34647         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34648         this.overlay.show();
34649         Roo.get(this.proxy).setDisplayed("block");
34650         var size = this.adapter.getElementSize(this);
34651         this.activeMinSize = this.getMinimumSize();;
34652         this.activeMaxSize = this.getMaximumSize();;
34653         var c1 = size - this.activeMinSize;
34654         var c2 = Math.max(this.activeMaxSize - size, 0);
34655         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34656             this.dd.resetConstraints();
34657             this.dd.setXConstraint(
34658                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34659                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34660             );
34661             this.dd.setYConstraint(0, 0);
34662         }else{
34663             this.dd.resetConstraints();
34664             this.dd.setXConstraint(0, 0);
34665             this.dd.setYConstraint(
34666                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34667                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34668             );
34669          }
34670         this.dragSpecs.startSize = size;
34671         this.dragSpecs.startPoint = [x, y];
34672         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34673     },
34674     
34675     /** 
34676      * @private Called after the drag operation by the DDProxy
34677      */
34678     onEndProxyDrag : function(e){
34679         Roo.get(this.proxy).setDisplayed(false);
34680         var endPoint = Roo.lib.Event.getXY(e);
34681         if(this.overlay){
34682             this.overlay.hide();
34683         }
34684         var newSize;
34685         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34686             newSize = this.dragSpecs.startSize + 
34687                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34688                     endPoint[0] - this.dragSpecs.startPoint[0] :
34689                     this.dragSpecs.startPoint[0] - endPoint[0]
34690                 );
34691         }else{
34692             newSize = this.dragSpecs.startSize + 
34693                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34694                     endPoint[1] - this.dragSpecs.startPoint[1] :
34695                     this.dragSpecs.startPoint[1] - endPoint[1]
34696                 );
34697         }
34698         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34699         if(newSize != this.dragSpecs.startSize){
34700             if(this.fireEvent('beforeapply', this, newSize) !== false){
34701                 this.adapter.setElementSize(this, newSize);
34702                 this.fireEvent("moved", this, newSize);
34703                 this.fireEvent("resize", this, newSize);
34704             }
34705         }
34706     },
34707     
34708     /**
34709      * Get the adapter this SplitBar uses
34710      * @return The adapter object
34711      */
34712     getAdapter : function(){
34713         return this.adapter;
34714     },
34715     
34716     /**
34717      * Set the adapter this SplitBar uses
34718      * @param {Object} adapter A SplitBar adapter object
34719      */
34720     setAdapter : function(adapter){
34721         this.adapter = adapter;
34722         this.adapter.init(this);
34723     },
34724     
34725     /**
34726      * Gets the minimum size for the resizing element
34727      * @return {Number} The minimum size
34728      */
34729     getMinimumSize : function(){
34730         return this.minSize;
34731     },
34732     
34733     /**
34734      * Sets the minimum size for the resizing element
34735      * @param {Number} minSize The minimum size
34736      */
34737     setMinimumSize : function(minSize){
34738         this.minSize = minSize;
34739     },
34740     
34741     /**
34742      * Gets the maximum size for the resizing element
34743      * @return {Number} The maximum size
34744      */
34745     getMaximumSize : function(){
34746         return this.maxSize;
34747     },
34748     
34749     /**
34750      * Sets the maximum size for the resizing element
34751      * @param {Number} maxSize The maximum size
34752      */
34753     setMaximumSize : function(maxSize){
34754         this.maxSize = maxSize;
34755     },
34756     
34757     /**
34758      * Sets the initialize size for the resizing element
34759      * @param {Number} size The initial size
34760      */
34761     setCurrentSize : function(size){
34762         var oldAnimate = this.animate;
34763         this.animate = false;
34764         this.adapter.setElementSize(this, size);
34765         this.animate = oldAnimate;
34766     },
34767     
34768     /**
34769      * Destroy this splitbar. 
34770      * @param {Boolean} removeEl True to remove the element
34771      */
34772     destroy : function(removeEl){
34773         if(this.shim){
34774             this.shim.remove();
34775         }
34776         this.dd.unreg();
34777         this.proxy.parentNode.removeChild(this.proxy);
34778         if(removeEl){
34779             this.el.remove();
34780         }
34781     }
34782 });
34783
34784 /**
34785  * @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.
34786  */
34787 Roo.bootstrap.SplitBar.createProxy = function(dir){
34788     var proxy = new Roo.Element(document.createElement("div"));
34789     proxy.unselectable();
34790     var cls = 'roo-splitbar-proxy';
34791     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34792     document.body.appendChild(proxy.dom);
34793     return proxy.dom;
34794 };
34795
34796 /** 
34797  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34798  * Default Adapter. It assumes the splitter and resizing element are not positioned
34799  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34800  */
34801 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34802 };
34803
34804 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34805     // do nothing for now
34806     init : function(s){
34807     
34808     },
34809     /**
34810      * Called before drag operations to get the current size of the resizing element. 
34811      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34812      */
34813      getElementSize : function(s){
34814         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34815             return s.resizingEl.getWidth();
34816         }else{
34817             return s.resizingEl.getHeight();
34818         }
34819     },
34820     
34821     /**
34822      * Called after drag operations to set the size of the resizing element.
34823      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34824      * @param {Number} newSize The new size to set
34825      * @param {Function} onComplete A function to be invoked when resizing is complete
34826      */
34827     setElementSize : function(s, newSize, onComplete){
34828         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34829             if(!s.animate){
34830                 s.resizingEl.setWidth(newSize);
34831                 if(onComplete){
34832                     onComplete(s, newSize);
34833                 }
34834             }else{
34835                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34836             }
34837         }else{
34838             
34839             if(!s.animate){
34840                 s.resizingEl.setHeight(newSize);
34841                 if(onComplete){
34842                     onComplete(s, newSize);
34843                 }
34844             }else{
34845                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34846             }
34847         }
34848     }
34849 };
34850
34851 /** 
34852  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34853  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34854  * Adapter that  moves the splitter element to align with the resized sizing element. 
34855  * Used with an absolute positioned SplitBar.
34856  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34857  * document.body, make sure you assign an id to the body element.
34858  */
34859 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34860     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34861     this.container = Roo.get(container);
34862 };
34863
34864 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34865     init : function(s){
34866         this.basic.init(s);
34867     },
34868     
34869     getElementSize : function(s){
34870         return this.basic.getElementSize(s);
34871     },
34872     
34873     setElementSize : function(s, newSize, onComplete){
34874         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34875     },
34876     
34877     moveSplitter : function(s){
34878         var yes = Roo.bootstrap.SplitBar;
34879         switch(s.placement){
34880             case yes.LEFT:
34881                 s.el.setX(s.resizingEl.getRight());
34882                 break;
34883             case yes.RIGHT:
34884                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34885                 break;
34886             case yes.TOP:
34887                 s.el.setY(s.resizingEl.getBottom());
34888                 break;
34889             case yes.BOTTOM:
34890                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34891                 break;
34892         }
34893     }
34894 };
34895
34896 /**
34897  * Orientation constant - Create a vertical SplitBar
34898  * @static
34899  * @type Number
34900  */
34901 Roo.bootstrap.SplitBar.VERTICAL = 1;
34902
34903 /**
34904  * Orientation constant - Create a horizontal SplitBar
34905  * @static
34906  * @type Number
34907  */
34908 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34909
34910 /**
34911  * Placement constant - The resizing element is to the left of the splitter element
34912  * @static
34913  * @type Number
34914  */
34915 Roo.bootstrap.SplitBar.LEFT = 1;
34916
34917 /**
34918  * Placement constant - The resizing element is to the right of the splitter element
34919  * @static
34920  * @type Number
34921  */
34922 Roo.bootstrap.SplitBar.RIGHT = 2;
34923
34924 /**
34925  * Placement constant - The resizing element is positioned above the splitter element
34926  * @static
34927  * @type Number
34928  */
34929 Roo.bootstrap.SplitBar.TOP = 3;
34930
34931 /**
34932  * Placement constant - The resizing element is positioned under splitter element
34933  * @static
34934  * @type Number
34935  */
34936 Roo.bootstrap.SplitBar.BOTTOM = 4;
34937 Roo.namespace("Roo.bootstrap.layout");/*
34938  * Based on:
34939  * Ext JS Library 1.1.1
34940  * Copyright(c) 2006-2007, Ext JS, LLC.
34941  *
34942  * Originally Released Under LGPL - original licence link has changed is not relivant.
34943  *
34944  * Fork - LGPL
34945  * <script type="text/javascript">
34946  */
34947
34948 /**
34949  * @class Roo.bootstrap.layout.Manager
34950  * @extends Roo.bootstrap.Component
34951  * Base class for layout managers.
34952  */
34953 Roo.bootstrap.layout.Manager = function(config)
34954 {
34955     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34956
34957
34958
34959
34960
34961     /** false to disable window resize monitoring @type Boolean */
34962     this.monitorWindowResize = true;
34963     this.regions = {};
34964     this.addEvents({
34965         /**
34966          * @event layout
34967          * Fires when a layout is performed.
34968          * @param {Roo.LayoutManager} this
34969          */
34970         "layout" : true,
34971         /**
34972          * @event regionresized
34973          * Fires when the user resizes a region.
34974          * @param {Roo.LayoutRegion} region The resized region
34975          * @param {Number} newSize The new size (width for east/west, height for north/south)
34976          */
34977         "regionresized" : true,
34978         /**
34979          * @event regioncollapsed
34980          * Fires when a region is collapsed.
34981          * @param {Roo.LayoutRegion} region The collapsed region
34982          */
34983         "regioncollapsed" : true,
34984         /**
34985          * @event regionexpanded
34986          * Fires when a region is expanded.
34987          * @param {Roo.LayoutRegion} region The expanded region
34988          */
34989         "regionexpanded" : true
34990     });
34991     this.updating = false;
34992
34993     if (config.el) {
34994         this.el = Roo.get(config.el);
34995         this.initEvents();
34996     }
34997
34998 };
34999
35000 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
35001
35002
35003     regions : null,
35004
35005     monitorWindowResize : true,
35006
35007
35008     updating : false,
35009
35010
35011     onRender : function(ct, position)
35012     {
35013         if(!this.el){
35014             this.el = Roo.get(ct);
35015             this.initEvents();
35016         }
35017         //this.fireEvent('render',this);
35018     },
35019
35020
35021     initEvents: function()
35022     {
35023
35024
35025         // ie scrollbar fix
35026         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
35027             document.body.scroll = "no";
35028         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
35029             this.el.position('relative');
35030         }
35031         this.id = this.el.id;
35032         this.el.addClass("roo-layout-container");
35033         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
35034         if(this.el.dom != document.body ) {
35035             this.el.on('resize', this.layout,this);
35036             this.el.on('show', this.layout,this);
35037         }
35038
35039     },
35040
35041     /**
35042      * Returns true if this layout is currently being updated
35043      * @return {Boolean}
35044      */
35045     isUpdating : function(){
35046         return this.updating;
35047     },
35048
35049     /**
35050      * Suspend the LayoutManager from doing auto-layouts while
35051      * making multiple add or remove calls
35052      */
35053     beginUpdate : function(){
35054         this.updating = true;
35055     },
35056
35057     /**
35058      * Restore auto-layouts and optionally disable the manager from performing a layout
35059      * @param {Boolean} noLayout true to disable a layout update
35060      */
35061     endUpdate : function(noLayout){
35062         this.updating = false;
35063         if(!noLayout){
35064             this.layout();
35065         }
35066     },
35067
35068     layout: function(){
35069         // abstract...
35070     },
35071
35072     onRegionResized : function(region, newSize){
35073         this.fireEvent("regionresized", region, newSize);
35074         this.layout();
35075     },
35076
35077     onRegionCollapsed : function(region){
35078         this.fireEvent("regioncollapsed", region);
35079     },
35080
35081     onRegionExpanded : function(region){
35082         this.fireEvent("regionexpanded", region);
35083     },
35084
35085     /**
35086      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35087      * performs box-model adjustments.
35088      * @return {Object} The size as an object {width: (the width), height: (the height)}
35089      */
35090     getViewSize : function()
35091     {
35092         var size;
35093         if(this.el.dom != document.body){
35094             size = this.el.getSize();
35095         }else{
35096             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35097         }
35098         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35099         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35100         return size;
35101     },
35102
35103     /**
35104      * Returns the Element this layout is bound to.
35105      * @return {Roo.Element}
35106      */
35107     getEl : function(){
35108         return this.el;
35109     },
35110
35111     /**
35112      * Returns the specified region.
35113      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35114      * @return {Roo.LayoutRegion}
35115      */
35116     getRegion : function(target){
35117         return this.regions[target.toLowerCase()];
35118     },
35119
35120     onWindowResize : function(){
35121         if(this.monitorWindowResize){
35122             this.layout();
35123         }
35124     }
35125 });
35126 /*
35127  * Based on:
35128  * Ext JS Library 1.1.1
35129  * Copyright(c) 2006-2007, Ext JS, LLC.
35130  *
35131  * Originally Released Under LGPL - original licence link has changed is not relivant.
35132  *
35133  * Fork - LGPL
35134  * <script type="text/javascript">
35135  */
35136 /**
35137  * @class Roo.bootstrap.layout.Border
35138  * @extends Roo.bootstrap.layout.Manager
35139  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35140  * please see: examples/bootstrap/nested.html<br><br>
35141  
35142 <b>The container the layout is rendered into can be either the body element or any other element.
35143 If it is not the body element, the container needs to either be an absolute positioned element,
35144 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35145 the container size if it is not the body element.</b>
35146
35147 * @constructor
35148 * Create a new Border
35149 * @param {Object} config Configuration options
35150  */
35151 Roo.bootstrap.layout.Border = function(config){
35152     config = config || {};
35153     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35154     
35155     
35156     
35157     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35158         if(config[region]){
35159             config[region].region = region;
35160             this.addRegion(config[region]);
35161         }
35162     },this);
35163     
35164 };
35165
35166 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35167
35168 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35169     
35170     parent : false, // this might point to a 'nest' or a ???
35171     
35172     /**
35173      * Creates and adds a new region if it doesn't already exist.
35174      * @param {String} target The target region key (north, south, east, west or center).
35175      * @param {Object} config The regions config object
35176      * @return {BorderLayoutRegion} The new region
35177      */
35178     addRegion : function(config)
35179     {
35180         if(!this.regions[config.region]){
35181             var r = this.factory(config);
35182             this.bindRegion(r);
35183         }
35184         return this.regions[config.region];
35185     },
35186
35187     // private (kinda)
35188     bindRegion : function(r){
35189         this.regions[r.config.region] = r;
35190         
35191         r.on("visibilitychange",    this.layout, this);
35192         r.on("paneladded",          this.layout, this);
35193         r.on("panelremoved",        this.layout, this);
35194         r.on("invalidated",         this.layout, this);
35195         r.on("resized",             this.onRegionResized, this);
35196         r.on("collapsed",           this.onRegionCollapsed, this);
35197         r.on("expanded",            this.onRegionExpanded, this);
35198     },
35199
35200     /**
35201      * Performs a layout update.
35202      */
35203     layout : function()
35204     {
35205         if(this.updating) {
35206             return;
35207         }
35208         
35209         // render all the rebions if they have not been done alreayd?
35210         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35211             if(this.regions[region] && !this.regions[region].bodyEl){
35212                 this.regions[region].onRender(this.el)
35213             }
35214         },this);
35215         
35216         var size = this.getViewSize();
35217         var w = size.width;
35218         var h = size.height;
35219         var centerW = w;
35220         var centerH = h;
35221         var centerY = 0;
35222         var centerX = 0;
35223         //var x = 0, y = 0;
35224
35225         var rs = this.regions;
35226         var north = rs["north"];
35227         var south = rs["south"]; 
35228         var west = rs["west"];
35229         var east = rs["east"];
35230         var center = rs["center"];
35231         //if(this.hideOnLayout){ // not supported anymore
35232             //c.el.setStyle("display", "none");
35233         //}
35234         if(north && north.isVisible()){
35235             var b = north.getBox();
35236             var m = north.getMargins();
35237             b.width = w - (m.left+m.right);
35238             b.x = m.left;
35239             b.y = m.top;
35240             centerY = b.height + b.y + m.bottom;
35241             centerH -= centerY;
35242             north.updateBox(this.safeBox(b));
35243         }
35244         if(south && south.isVisible()){
35245             var b = south.getBox();
35246             var m = south.getMargins();
35247             b.width = w - (m.left+m.right);
35248             b.x = m.left;
35249             var totalHeight = (b.height + m.top + m.bottom);
35250             b.y = h - totalHeight + m.top;
35251             centerH -= totalHeight;
35252             south.updateBox(this.safeBox(b));
35253         }
35254         if(west && west.isVisible()){
35255             var b = west.getBox();
35256             var m = west.getMargins();
35257             b.height = centerH - (m.top+m.bottom);
35258             b.x = m.left;
35259             b.y = centerY + m.top;
35260             var totalWidth = (b.width + m.left + m.right);
35261             centerX += totalWidth;
35262             centerW -= totalWidth;
35263             west.updateBox(this.safeBox(b));
35264         }
35265         if(east && east.isVisible()){
35266             var b = east.getBox();
35267             var m = east.getMargins();
35268             b.height = centerH - (m.top+m.bottom);
35269             var totalWidth = (b.width + m.left + m.right);
35270             b.x = w - totalWidth + m.left;
35271             b.y = centerY + m.top;
35272             centerW -= totalWidth;
35273             east.updateBox(this.safeBox(b));
35274         }
35275         if(center){
35276             var m = center.getMargins();
35277             var centerBox = {
35278                 x: centerX + m.left,
35279                 y: centerY + m.top,
35280                 width: centerW - (m.left+m.right),
35281                 height: centerH - (m.top+m.bottom)
35282             };
35283             //if(this.hideOnLayout){
35284                 //center.el.setStyle("display", "block");
35285             //}
35286             center.updateBox(this.safeBox(centerBox));
35287         }
35288         this.el.repaint();
35289         this.fireEvent("layout", this);
35290     },
35291
35292     // private
35293     safeBox : function(box){
35294         box.width = Math.max(0, box.width);
35295         box.height = Math.max(0, box.height);
35296         return box;
35297     },
35298
35299     /**
35300      * Adds a ContentPanel (or subclass) to this layout.
35301      * @param {String} target The target region key (north, south, east, west or center).
35302      * @param {Roo.ContentPanel} panel The panel to add
35303      * @return {Roo.ContentPanel} The added panel
35304      */
35305     add : function(target, panel){
35306          
35307         target = target.toLowerCase();
35308         return this.regions[target].add(panel);
35309     },
35310
35311     /**
35312      * Remove a ContentPanel (or subclass) to this layout.
35313      * @param {String} target The target region key (north, south, east, west or center).
35314      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35315      * @return {Roo.ContentPanel} The removed panel
35316      */
35317     remove : function(target, panel){
35318         target = target.toLowerCase();
35319         return this.regions[target].remove(panel);
35320     },
35321
35322     /**
35323      * Searches all regions for a panel with the specified id
35324      * @param {String} panelId
35325      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35326      */
35327     findPanel : function(panelId){
35328         var rs = this.regions;
35329         for(var target in rs){
35330             if(typeof rs[target] != "function"){
35331                 var p = rs[target].getPanel(panelId);
35332                 if(p){
35333                     return p;
35334                 }
35335             }
35336         }
35337         return null;
35338     },
35339
35340     /**
35341      * Searches all regions for a panel with the specified id and activates (shows) it.
35342      * @param {String/ContentPanel} panelId The panels id or the panel itself
35343      * @return {Roo.ContentPanel} The shown panel or null
35344      */
35345     showPanel : function(panelId) {
35346       var rs = this.regions;
35347       for(var target in rs){
35348          var r = rs[target];
35349          if(typeof r != "function"){
35350             if(r.hasPanel(panelId)){
35351                return r.showPanel(panelId);
35352             }
35353          }
35354       }
35355       return null;
35356    },
35357
35358    /**
35359      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35360      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35361      */
35362    /*
35363     restoreState : function(provider){
35364         if(!provider){
35365             provider = Roo.state.Manager;
35366         }
35367         var sm = new Roo.LayoutStateManager();
35368         sm.init(this, provider);
35369     },
35370 */
35371  
35372  
35373     /**
35374      * Adds a xtype elements to the layout.
35375      * <pre><code>
35376
35377 layout.addxtype({
35378        xtype : 'ContentPanel',
35379        region: 'west',
35380        items: [ .... ]
35381    }
35382 );
35383
35384 layout.addxtype({
35385         xtype : 'NestedLayoutPanel',
35386         region: 'west',
35387         layout: {
35388            center: { },
35389            west: { }   
35390         },
35391         items : [ ... list of content panels or nested layout panels.. ]
35392    }
35393 );
35394 </code></pre>
35395      * @param {Object} cfg Xtype definition of item to add.
35396      */
35397     addxtype : function(cfg)
35398     {
35399         // basically accepts a pannel...
35400         // can accept a layout region..!?!?
35401         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35402         
35403         
35404         // theory?  children can only be panels??
35405         
35406         //if (!cfg.xtype.match(/Panel$/)) {
35407         //    return false;
35408         //}
35409         var ret = false;
35410         
35411         if (typeof(cfg.region) == 'undefined') {
35412             Roo.log("Failed to add Panel, region was not set");
35413             Roo.log(cfg);
35414             return false;
35415         }
35416         var region = cfg.region;
35417         delete cfg.region;
35418         
35419           
35420         var xitems = [];
35421         if (cfg.items) {
35422             xitems = cfg.items;
35423             delete cfg.items;
35424         }
35425         var nb = false;
35426         
35427         if ( region == 'center') {
35428             Roo.log("Center: " + cfg.title);
35429         }
35430         
35431         
35432         switch(cfg.xtype) 
35433         {
35434             case 'Content':  // ContentPanel (el, cfg)
35435             case 'Scroll':  // ContentPanel (el, cfg)
35436             case 'View': 
35437                 cfg.autoCreate = cfg.autoCreate || true;
35438                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35439                 //} else {
35440                 //    var el = this.el.createChild();
35441                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35442                 //}
35443                 
35444                 this.add(region, ret);
35445                 break;
35446             
35447             /*
35448             case 'TreePanel': // our new panel!
35449                 cfg.el = this.el.createChild();
35450                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35451                 this.add(region, ret);
35452                 break;
35453             */
35454             
35455             case 'Nest': 
35456                 // create a new Layout (which is  a Border Layout...
35457                 
35458                 var clayout = cfg.layout;
35459                 clayout.el  = this.el.createChild();
35460                 clayout.items   = clayout.items  || [];
35461                 
35462                 delete cfg.layout;
35463                 
35464                 // replace this exitems with the clayout ones..
35465                 xitems = clayout.items;
35466                  
35467                 // force background off if it's in center...
35468                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35469                     cfg.background = false;
35470                 }
35471                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35472                 
35473                 
35474                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35475                 //console.log('adding nested layout panel '  + cfg.toSource());
35476                 this.add(region, ret);
35477                 nb = {}; /// find first...
35478                 break;
35479             
35480             case 'Grid':
35481                 
35482                 // needs grid and region
35483                 
35484                 //var el = this.getRegion(region).el.createChild();
35485                 /*
35486                  *var el = this.el.createChild();
35487                 // create the grid first...
35488                 cfg.grid.container = el;
35489                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35490                 */
35491                 
35492                 if (region == 'center' && this.active ) {
35493                     cfg.background = false;
35494                 }
35495                 
35496                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35497                 
35498                 this.add(region, ret);
35499                 /*
35500                 if (cfg.background) {
35501                     // render grid on panel activation (if panel background)
35502                     ret.on('activate', function(gp) {
35503                         if (!gp.grid.rendered) {
35504                     //        gp.grid.render(el);
35505                         }
35506                     });
35507                 } else {
35508                   //  cfg.grid.render(el);
35509                 }
35510                 */
35511                 break;
35512            
35513            
35514             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35515                 // it was the old xcomponent building that caused this before.
35516                 // espeically if border is the top element in the tree.
35517                 ret = this;
35518                 break; 
35519                 
35520                     
35521                 
35522                 
35523                 
35524             default:
35525                 /*
35526                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35527                     
35528                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35529                     this.add(region, ret);
35530                 } else {
35531                 */
35532                     Roo.log(cfg);
35533                     throw "Can not add '" + cfg.xtype + "' to Border";
35534                     return null;
35535              
35536                                 
35537              
35538         }
35539         this.beginUpdate();
35540         // add children..
35541         var region = '';
35542         var abn = {};
35543         Roo.each(xitems, function(i)  {
35544             region = nb && i.region ? i.region : false;
35545             
35546             var add = ret.addxtype(i);
35547            
35548             if (region) {
35549                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35550                 if (!i.background) {
35551                     abn[region] = nb[region] ;
35552                 }
35553             }
35554             
35555         });
35556         this.endUpdate();
35557
35558         // make the last non-background panel active..
35559         //if (nb) { Roo.log(abn); }
35560         if (nb) {
35561             
35562             for(var r in abn) {
35563                 region = this.getRegion(r);
35564                 if (region) {
35565                     // tried using nb[r], but it does not work..
35566                      
35567                     region.showPanel(abn[r]);
35568                    
35569                 }
35570             }
35571         }
35572         return ret;
35573         
35574     },
35575     
35576     
35577 // private
35578     factory : function(cfg)
35579     {
35580         
35581         var validRegions = Roo.bootstrap.layout.Border.regions;
35582
35583         var target = cfg.region;
35584         cfg.mgr = this;
35585         
35586         var r = Roo.bootstrap.layout;
35587         Roo.log(target);
35588         switch(target){
35589             case "north":
35590                 return new r.North(cfg);
35591             case "south":
35592                 return new r.South(cfg);
35593             case "east":
35594                 return new r.East(cfg);
35595             case "west":
35596                 return new r.West(cfg);
35597             case "center":
35598                 return new r.Center(cfg);
35599         }
35600         throw 'Layout region "'+target+'" not supported.';
35601     }
35602     
35603     
35604 });
35605  /*
35606  * Based on:
35607  * Ext JS Library 1.1.1
35608  * Copyright(c) 2006-2007, Ext JS, LLC.
35609  *
35610  * Originally Released Under LGPL - original licence link has changed is not relivant.
35611  *
35612  * Fork - LGPL
35613  * <script type="text/javascript">
35614  */
35615  
35616 /**
35617  * @class Roo.bootstrap.layout.Basic
35618  * @extends Roo.util.Observable
35619  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35620  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35621  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35622  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35623  * @cfg {string}   region  the region that it inhabits..
35624  * @cfg {bool}   skipConfig skip config?
35625  * 
35626
35627  */
35628 Roo.bootstrap.layout.Basic = function(config){
35629     
35630     this.mgr = config.mgr;
35631     
35632     this.position = config.region;
35633     
35634     var skipConfig = config.skipConfig;
35635     
35636     this.events = {
35637         /**
35638          * @scope Roo.BasicLayoutRegion
35639          */
35640         
35641         /**
35642          * @event beforeremove
35643          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35644          * @param {Roo.LayoutRegion} this
35645          * @param {Roo.ContentPanel} panel The panel
35646          * @param {Object} e The cancel event object
35647          */
35648         "beforeremove" : true,
35649         /**
35650          * @event invalidated
35651          * Fires when the layout for this region is changed.
35652          * @param {Roo.LayoutRegion} this
35653          */
35654         "invalidated" : true,
35655         /**
35656          * @event visibilitychange
35657          * Fires when this region is shown or hidden 
35658          * @param {Roo.LayoutRegion} this
35659          * @param {Boolean} visibility true or false
35660          */
35661         "visibilitychange" : true,
35662         /**
35663          * @event paneladded
35664          * Fires when a panel is added. 
35665          * @param {Roo.LayoutRegion} this
35666          * @param {Roo.ContentPanel} panel The panel
35667          */
35668         "paneladded" : true,
35669         /**
35670          * @event panelremoved
35671          * Fires when a panel is removed. 
35672          * @param {Roo.LayoutRegion} this
35673          * @param {Roo.ContentPanel} panel The panel
35674          */
35675         "panelremoved" : true,
35676         /**
35677          * @event beforecollapse
35678          * Fires when this region before collapse.
35679          * @param {Roo.LayoutRegion} this
35680          */
35681         "beforecollapse" : true,
35682         /**
35683          * @event collapsed
35684          * Fires when this region is collapsed.
35685          * @param {Roo.LayoutRegion} this
35686          */
35687         "collapsed" : true,
35688         /**
35689          * @event expanded
35690          * Fires when this region is expanded.
35691          * @param {Roo.LayoutRegion} this
35692          */
35693         "expanded" : true,
35694         /**
35695          * @event slideshow
35696          * Fires when this region is slid into view.
35697          * @param {Roo.LayoutRegion} this
35698          */
35699         "slideshow" : true,
35700         /**
35701          * @event slidehide
35702          * Fires when this region slides out of view. 
35703          * @param {Roo.LayoutRegion} this
35704          */
35705         "slidehide" : true,
35706         /**
35707          * @event panelactivated
35708          * Fires when a panel is activated. 
35709          * @param {Roo.LayoutRegion} this
35710          * @param {Roo.ContentPanel} panel The activated panel
35711          */
35712         "panelactivated" : true,
35713         /**
35714          * @event resized
35715          * Fires when the user resizes this region. 
35716          * @param {Roo.LayoutRegion} this
35717          * @param {Number} newSize The new size (width for east/west, height for north/south)
35718          */
35719         "resized" : true
35720     };
35721     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35722     this.panels = new Roo.util.MixedCollection();
35723     this.panels.getKey = this.getPanelId.createDelegate(this);
35724     this.box = null;
35725     this.activePanel = null;
35726     // ensure listeners are added...
35727     
35728     if (config.listeners || config.events) {
35729         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35730             listeners : config.listeners || {},
35731             events : config.events || {}
35732         });
35733     }
35734     
35735     if(skipConfig !== true){
35736         this.applyConfig(config);
35737     }
35738 };
35739
35740 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35741 {
35742     getPanelId : function(p){
35743         return p.getId();
35744     },
35745     
35746     applyConfig : function(config){
35747         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35748         this.config = config;
35749         
35750     },
35751     
35752     /**
35753      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35754      * the width, for horizontal (north, south) the height.
35755      * @param {Number} newSize The new width or height
35756      */
35757     resizeTo : function(newSize){
35758         var el = this.el ? this.el :
35759                  (this.activePanel ? this.activePanel.getEl() : null);
35760         if(el){
35761             switch(this.position){
35762                 case "east":
35763                 case "west":
35764                     el.setWidth(newSize);
35765                     this.fireEvent("resized", this, newSize);
35766                 break;
35767                 case "north":
35768                 case "south":
35769                     el.setHeight(newSize);
35770                     this.fireEvent("resized", this, newSize);
35771                 break;                
35772             }
35773         }
35774     },
35775     
35776     getBox : function(){
35777         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35778     },
35779     
35780     getMargins : function(){
35781         return this.margins;
35782     },
35783     
35784     updateBox : function(box){
35785         this.box = box;
35786         var el = this.activePanel.getEl();
35787         el.dom.style.left = box.x + "px";
35788         el.dom.style.top = box.y + "px";
35789         this.activePanel.setSize(box.width, box.height);
35790     },
35791     
35792     /**
35793      * Returns the container element for this region.
35794      * @return {Roo.Element}
35795      */
35796     getEl : function(){
35797         return this.activePanel;
35798     },
35799     
35800     /**
35801      * Returns true if this region is currently visible.
35802      * @return {Boolean}
35803      */
35804     isVisible : function(){
35805         return this.activePanel ? true : false;
35806     },
35807     
35808     setActivePanel : function(panel){
35809         panel = this.getPanel(panel);
35810         if(this.activePanel && this.activePanel != panel){
35811             this.activePanel.setActiveState(false);
35812             this.activePanel.getEl().setLeftTop(-10000,-10000);
35813         }
35814         this.activePanel = panel;
35815         panel.setActiveState(true);
35816         if(this.box){
35817             panel.setSize(this.box.width, this.box.height);
35818         }
35819         this.fireEvent("panelactivated", this, panel);
35820         this.fireEvent("invalidated");
35821     },
35822     
35823     /**
35824      * Show the specified panel.
35825      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35826      * @return {Roo.ContentPanel} The shown panel or null
35827      */
35828     showPanel : function(panel){
35829         panel = this.getPanel(panel);
35830         if(panel){
35831             this.setActivePanel(panel);
35832         }
35833         return panel;
35834     },
35835     
35836     /**
35837      * Get the active panel for this region.
35838      * @return {Roo.ContentPanel} The active panel or null
35839      */
35840     getActivePanel : function(){
35841         return this.activePanel;
35842     },
35843     
35844     /**
35845      * Add the passed ContentPanel(s)
35846      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35847      * @return {Roo.ContentPanel} The panel added (if only one was added)
35848      */
35849     add : function(panel){
35850         if(arguments.length > 1){
35851             for(var i = 0, len = arguments.length; i < len; i++) {
35852                 this.add(arguments[i]);
35853             }
35854             return null;
35855         }
35856         if(this.hasPanel(panel)){
35857             this.showPanel(panel);
35858             return panel;
35859         }
35860         var el = panel.getEl();
35861         if(el.dom.parentNode != this.mgr.el.dom){
35862             this.mgr.el.dom.appendChild(el.dom);
35863         }
35864         if(panel.setRegion){
35865             panel.setRegion(this);
35866         }
35867         this.panels.add(panel);
35868         el.setStyle("position", "absolute");
35869         if(!panel.background){
35870             this.setActivePanel(panel);
35871             if(this.config.initialSize && this.panels.getCount()==1){
35872                 this.resizeTo(this.config.initialSize);
35873             }
35874         }
35875         this.fireEvent("paneladded", this, panel);
35876         return panel;
35877     },
35878     
35879     /**
35880      * Returns true if the panel is in this region.
35881      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35882      * @return {Boolean}
35883      */
35884     hasPanel : function(panel){
35885         if(typeof panel == "object"){ // must be panel obj
35886             panel = panel.getId();
35887         }
35888         return this.getPanel(panel) ? true : false;
35889     },
35890     
35891     /**
35892      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35893      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35894      * @param {Boolean} preservePanel Overrides the config preservePanel option
35895      * @return {Roo.ContentPanel} The panel that was removed
35896      */
35897     remove : function(panel, preservePanel){
35898         panel = this.getPanel(panel);
35899         if(!panel){
35900             return null;
35901         }
35902         var e = {};
35903         this.fireEvent("beforeremove", this, panel, e);
35904         if(e.cancel === true){
35905             return null;
35906         }
35907         var panelId = panel.getId();
35908         this.panels.removeKey(panelId);
35909         return panel;
35910     },
35911     
35912     /**
35913      * Returns the panel specified or null if it's not in this region.
35914      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35915      * @return {Roo.ContentPanel}
35916      */
35917     getPanel : function(id){
35918         if(typeof id == "object"){ // must be panel obj
35919             return id;
35920         }
35921         return this.panels.get(id);
35922     },
35923     
35924     /**
35925      * Returns this regions position (north/south/east/west/center).
35926      * @return {String} 
35927      */
35928     getPosition: function(){
35929         return this.position;    
35930     }
35931 });/*
35932  * Based on:
35933  * Ext JS Library 1.1.1
35934  * Copyright(c) 2006-2007, Ext JS, LLC.
35935  *
35936  * Originally Released Under LGPL - original licence link has changed is not relivant.
35937  *
35938  * Fork - LGPL
35939  * <script type="text/javascript">
35940  */
35941  
35942 /**
35943  * @class Roo.bootstrap.layout.Region
35944  * @extends Roo.bootstrap.layout.Basic
35945  * This class represents a region in a layout manager.
35946  
35947  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35948  * @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})
35949  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35950  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35951  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35952  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35953  * @cfg {String}    title           The title for the region (overrides panel titles)
35954  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35955  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35956  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35957  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35958  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35959  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35960  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35961  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35962  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35963  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35964
35965  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35966  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35967  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35968  * @cfg {Number}    width           For East/West panels
35969  * @cfg {Number}    height          For North/South panels
35970  * @cfg {Boolean}   split           To show the splitter
35971  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35972  * 
35973  * @cfg {string}   cls             Extra CSS classes to add to region
35974  * 
35975  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35976  * @cfg {string}   region  the region that it inhabits..
35977  *
35978
35979  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35980  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35981
35982  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35983  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35984  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35985  */
35986 Roo.bootstrap.layout.Region = function(config)
35987 {
35988     this.applyConfig(config);
35989
35990     var mgr = config.mgr;
35991     var pos = config.region;
35992     config.skipConfig = true;
35993     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35994     
35995     if (mgr.el) {
35996         this.onRender(mgr.el);   
35997     }
35998      
35999     this.visible = true;
36000     this.collapsed = false;
36001     this.unrendered_panels = [];
36002 };
36003
36004 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
36005
36006     position: '', // set by wrapper (eg. north/south etc..)
36007     unrendered_panels : null,  // unrendered panels.
36008     
36009     tabPosition : false,
36010     
36011     mgr: false, // points to 'Border'
36012     
36013     
36014     createBody : function(){
36015         /** This region's body element 
36016         * @type Roo.Element */
36017         this.bodyEl = this.el.createChild({
36018                 tag: "div",
36019                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
36020         });
36021     },
36022
36023     onRender: function(ctr, pos)
36024     {
36025         var dh = Roo.DomHelper;
36026         /** This region's container element 
36027         * @type Roo.Element */
36028         this.el = dh.append(ctr.dom, {
36029                 tag: "div",
36030                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
36031             }, true);
36032         /** This region's title element 
36033         * @type Roo.Element */
36034     
36035         this.titleEl = dh.append(this.el.dom,  {
36036                 tag: "div",
36037                 unselectable: "on",
36038                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
36039                 children:[
36040                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
36041                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36042                 ]
36043             }, true);
36044         
36045         this.titleEl.enableDisplayMode();
36046         /** This region's title text element 
36047         * @type HTMLElement */
36048         this.titleTextEl = this.titleEl.dom.firstChild;
36049         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36050         /*
36051         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36052         this.closeBtn.enableDisplayMode();
36053         this.closeBtn.on("click", this.closeClicked, this);
36054         this.closeBtn.hide();
36055     */
36056         this.createBody(this.config);
36057         if(this.config.hideWhenEmpty){
36058             this.hide();
36059             this.on("paneladded", this.validateVisibility, this);
36060             this.on("panelremoved", this.validateVisibility, this);
36061         }
36062         if(this.autoScroll){
36063             this.bodyEl.setStyle("overflow", "auto");
36064         }else{
36065             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36066         }
36067         //if(c.titlebar !== false){
36068             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36069                 this.titleEl.hide();
36070             }else{
36071                 this.titleEl.show();
36072                 if(this.config.title){
36073                     this.titleTextEl.innerHTML = this.config.title;
36074                 }
36075             }
36076         //}
36077         if(this.config.collapsed){
36078             this.collapse(true);
36079         }
36080         if(this.config.hidden){
36081             this.hide();
36082         }
36083         
36084         if (this.unrendered_panels && this.unrendered_panels.length) {
36085             for (var i =0;i< this.unrendered_panels.length; i++) {
36086                 this.add(this.unrendered_panels[i]);
36087             }
36088             this.unrendered_panels = null;
36089             
36090         }
36091         
36092     },
36093     
36094     applyConfig : function(c)
36095     {
36096         /*
36097          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36098             var dh = Roo.DomHelper;
36099             if(c.titlebar !== false){
36100                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36101                 this.collapseBtn.on("click", this.collapse, this);
36102                 this.collapseBtn.enableDisplayMode();
36103                 /*
36104                 if(c.showPin === true || this.showPin){
36105                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36106                     this.stickBtn.enableDisplayMode();
36107                     this.stickBtn.on("click", this.expand, this);
36108                     this.stickBtn.hide();
36109                 }
36110                 
36111             }
36112             */
36113             /** This region's collapsed element
36114             * @type Roo.Element */
36115             /*
36116              *
36117             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36118                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36119             ]}, true);
36120             
36121             if(c.floatable !== false){
36122                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36123                this.collapsedEl.on("click", this.collapseClick, this);
36124             }
36125
36126             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36127                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36128                    id: "message", unselectable: "on", style:{"float":"left"}});
36129                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36130              }
36131             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36132             this.expandBtn.on("click", this.expand, this);
36133             
36134         }
36135         
36136         if(this.collapseBtn){
36137             this.collapseBtn.setVisible(c.collapsible == true);
36138         }
36139         
36140         this.cmargins = c.cmargins || this.cmargins ||
36141                          (this.position == "west" || this.position == "east" ?
36142                              {top: 0, left: 2, right:2, bottom: 0} :
36143                              {top: 2, left: 0, right:0, bottom: 2});
36144         */
36145         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36146         
36147         
36148         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36149         
36150         this.autoScroll = c.autoScroll || false;
36151         
36152         
36153        
36154         
36155         this.duration = c.duration || .30;
36156         this.slideDuration = c.slideDuration || .45;
36157         this.config = c;
36158        
36159     },
36160     /**
36161      * Returns true if this region is currently visible.
36162      * @return {Boolean}
36163      */
36164     isVisible : function(){
36165         return this.visible;
36166     },
36167
36168     /**
36169      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36170      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36171      */
36172     //setCollapsedTitle : function(title){
36173     //    title = title || "&#160;";
36174      //   if(this.collapsedTitleTextEl){
36175       //      this.collapsedTitleTextEl.innerHTML = title;
36176        // }
36177     //},
36178
36179     getBox : function(){
36180         var b;
36181       //  if(!this.collapsed){
36182             b = this.el.getBox(false, true);
36183        // }else{
36184           //  b = this.collapsedEl.getBox(false, true);
36185         //}
36186         return b;
36187     },
36188
36189     getMargins : function(){
36190         return this.margins;
36191         //return this.collapsed ? this.cmargins : this.margins;
36192     },
36193 /*
36194     highlight : function(){
36195         this.el.addClass("x-layout-panel-dragover");
36196     },
36197
36198     unhighlight : function(){
36199         this.el.removeClass("x-layout-panel-dragover");
36200     },
36201 */
36202     updateBox : function(box)
36203     {
36204         if (!this.bodyEl) {
36205             return; // not rendered yet..
36206         }
36207         
36208         this.box = box;
36209         if(!this.collapsed){
36210             this.el.dom.style.left = box.x + "px";
36211             this.el.dom.style.top = box.y + "px";
36212             this.updateBody(box.width, box.height);
36213         }else{
36214             this.collapsedEl.dom.style.left = box.x + "px";
36215             this.collapsedEl.dom.style.top = box.y + "px";
36216             this.collapsedEl.setSize(box.width, box.height);
36217         }
36218         if(this.tabs){
36219             this.tabs.autoSizeTabs();
36220         }
36221     },
36222
36223     updateBody : function(w, h)
36224     {
36225         if(w !== null){
36226             this.el.setWidth(w);
36227             w -= this.el.getBorderWidth("rl");
36228             if(this.config.adjustments){
36229                 w += this.config.adjustments[0];
36230             }
36231         }
36232         if(h !== null && h > 0){
36233             this.el.setHeight(h);
36234             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36235             h -= this.el.getBorderWidth("tb");
36236             if(this.config.adjustments){
36237                 h += this.config.adjustments[1];
36238             }
36239             this.bodyEl.setHeight(h);
36240             if(this.tabs){
36241                 h = this.tabs.syncHeight(h);
36242             }
36243         }
36244         if(this.panelSize){
36245             w = w !== null ? w : this.panelSize.width;
36246             h = h !== null ? h : this.panelSize.height;
36247         }
36248         if(this.activePanel){
36249             var el = this.activePanel.getEl();
36250             w = w !== null ? w : el.getWidth();
36251             h = h !== null ? h : el.getHeight();
36252             this.panelSize = {width: w, height: h};
36253             this.activePanel.setSize(w, h);
36254         }
36255         if(Roo.isIE && this.tabs){
36256             this.tabs.el.repaint();
36257         }
36258     },
36259
36260     /**
36261      * Returns the container element for this region.
36262      * @return {Roo.Element}
36263      */
36264     getEl : function(){
36265         return this.el;
36266     },
36267
36268     /**
36269      * Hides this region.
36270      */
36271     hide : function(){
36272         //if(!this.collapsed){
36273             this.el.dom.style.left = "-2000px";
36274             this.el.hide();
36275         //}else{
36276          //   this.collapsedEl.dom.style.left = "-2000px";
36277          //   this.collapsedEl.hide();
36278        // }
36279         this.visible = false;
36280         this.fireEvent("visibilitychange", this, false);
36281     },
36282
36283     /**
36284      * Shows this region if it was previously hidden.
36285      */
36286     show : function(){
36287         //if(!this.collapsed){
36288             this.el.show();
36289         //}else{
36290         //    this.collapsedEl.show();
36291        // }
36292         this.visible = true;
36293         this.fireEvent("visibilitychange", this, true);
36294     },
36295 /*
36296     closeClicked : function(){
36297         if(this.activePanel){
36298             this.remove(this.activePanel);
36299         }
36300     },
36301
36302     collapseClick : function(e){
36303         if(this.isSlid){
36304            e.stopPropagation();
36305            this.slideIn();
36306         }else{
36307            e.stopPropagation();
36308            this.slideOut();
36309         }
36310     },
36311 */
36312     /**
36313      * Collapses this region.
36314      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36315      */
36316     /*
36317     collapse : function(skipAnim, skipCheck = false){
36318         if(this.collapsed) {
36319             return;
36320         }
36321         
36322         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36323             
36324             this.collapsed = true;
36325             if(this.split){
36326                 this.split.el.hide();
36327             }
36328             if(this.config.animate && skipAnim !== true){
36329                 this.fireEvent("invalidated", this);
36330                 this.animateCollapse();
36331             }else{
36332                 this.el.setLocation(-20000,-20000);
36333                 this.el.hide();
36334                 this.collapsedEl.show();
36335                 this.fireEvent("collapsed", this);
36336                 this.fireEvent("invalidated", this);
36337             }
36338         }
36339         
36340     },
36341 */
36342     animateCollapse : function(){
36343         // overridden
36344     },
36345
36346     /**
36347      * Expands this region if it was previously collapsed.
36348      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36349      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36350      */
36351     /*
36352     expand : function(e, skipAnim){
36353         if(e) {
36354             e.stopPropagation();
36355         }
36356         if(!this.collapsed || this.el.hasActiveFx()) {
36357             return;
36358         }
36359         if(this.isSlid){
36360             this.afterSlideIn();
36361             skipAnim = true;
36362         }
36363         this.collapsed = false;
36364         if(this.config.animate && skipAnim !== true){
36365             this.animateExpand();
36366         }else{
36367             this.el.show();
36368             if(this.split){
36369                 this.split.el.show();
36370             }
36371             this.collapsedEl.setLocation(-2000,-2000);
36372             this.collapsedEl.hide();
36373             this.fireEvent("invalidated", this);
36374             this.fireEvent("expanded", this);
36375         }
36376     },
36377 */
36378     animateExpand : function(){
36379         // overridden
36380     },
36381
36382     initTabs : function()
36383     {
36384         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36385         
36386         var ts = new Roo.bootstrap.panel.Tabs({
36387             el: this.bodyEl.dom,
36388             region : this,
36389             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36390             disableTooltips: this.config.disableTabTips,
36391             toolbar : this.config.toolbar
36392         });
36393         
36394         if(this.config.hideTabs){
36395             ts.stripWrap.setDisplayed(false);
36396         }
36397         this.tabs = ts;
36398         ts.resizeTabs = this.config.resizeTabs === true;
36399         ts.minTabWidth = this.config.minTabWidth || 40;
36400         ts.maxTabWidth = this.config.maxTabWidth || 250;
36401         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36402         ts.monitorResize = false;
36403         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36404         ts.bodyEl.addClass('roo-layout-tabs-body');
36405         this.panels.each(this.initPanelAsTab, this);
36406     },
36407
36408     initPanelAsTab : function(panel){
36409         var ti = this.tabs.addTab(
36410             panel.getEl().id,
36411             panel.getTitle(),
36412             null,
36413             this.config.closeOnTab && panel.isClosable(),
36414             panel.tpl
36415         );
36416         if(panel.tabTip !== undefined){
36417             ti.setTooltip(panel.tabTip);
36418         }
36419         ti.on("activate", function(){
36420               this.setActivePanel(panel);
36421         }, this);
36422         
36423         if(this.config.closeOnTab){
36424             ti.on("beforeclose", function(t, e){
36425                 e.cancel = true;
36426                 this.remove(panel);
36427             }, this);
36428         }
36429         
36430         panel.tabItem = ti;
36431         
36432         return ti;
36433     },
36434
36435     updatePanelTitle : function(panel, title)
36436     {
36437         if(this.activePanel == panel){
36438             this.updateTitle(title);
36439         }
36440         if(this.tabs){
36441             var ti = this.tabs.getTab(panel.getEl().id);
36442             ti.setText(title);
36443             if(panel.tabTip !== undefined){
36444                 ti.setTooltip(panel.tabTip);
36445             }
36446         }
36447     },
36448
36449     updateTitle : function(title){
36450         if(this.titleTextEl && !this.config.title){
36451             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36452         }
36453     },
36454
36455     setActivePanel : function(panel)
36456     {
36457         panel = this.getPanel(panel);
36458         if(this.activePanel && this.activePanel != panel){
36459             if(this.activePanel.setActiveState(false) === false){
36460                 return;
36461             }
36462         }
36463         this.activePanel = panel;
36464         panel.setActiveState(true);
36465         if(this.panelSize){
36466             panel.setSize(this.panelSize.width, this.panelSize.height);
36467         }
36468         if(this.closeBtn){
36469             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36470         }
36471         this.updateTitle(panel.getTitle());
36472         if(this.tabs){
36473             this.fireEvent("invalidated", this);
36474         }
36475         this.fireEvent("panelactivated", this, panel);
36476     },
36477
36478     /**
36479      * Shows the specified panel.
36480      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36481      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36482      */
36483     showPanel : function(panel)
36484     {
36485         panel = this.getPanel(panel);
36486         if(panel){
36487             if(this.tabs){
36488                 var tab = this.tabs.getTab(panel.getEl().id);
36489                 if(tab.isHidden()){
36490                     this.tabs.unhideTab(tab.id);
36491                 }
36492                 tab.activate();
36493             }else{
36494                 this.setActivePanel(panel);
36495             }
36496         }
36497         return panel;
36498     },
36499
36500     /**
36501      * Get the active panel for this region.
36502      * @return {Roo.ContentPanel} The active panel or null
36503      */
36504     getActivePanel : function(){
36505         return this.activePanel;
36506     },
36507
36508     validateVisibility : function(){
36509         if(this.panels.getCount() < 1){
36510             this.updateTitle("&#160;");
36511             this.closeBtn.hide();
36512             this.hide();
36513         }else{
36514             if(!this.isVisible()){
36515                 this.show();
36516             }
36517         }
36518     },
36519
36520     /**
36521      * Adds the passed ContentPanel(s) to this region.
36522      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36523      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36524      */
36525     add : function(panel)
36526     {
36527         if(arguments.length > 1){
36528             for(var i = 0, len = arguments.length; i < len; i++) {
36529                 this.add(arguments[i]);
36530             }
36531             return null;
36532         }
36533         
36534         // if we have not been rendered yet, then we can not really do much of this..
36535         if (!this.bodyEl) {
36536             this.unrendered_panels.push(panel);
36537             return panel;
36538         }
36539         
36540         
36541         
36542         
36543         if(this.hasPanel(panel)){
36544             this.showPanel(panel);
36545             return panel;
36546         }
36547         panel.setRegion(this);
36548         this.panels.add(panel);
36549        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36550             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36551             // and hide them... ???
36552             this.bodyEl.dom.appendChild(panel.getEl().dom);
36553             if(panel.background !== true){
36554                 this.setActivePanel(panel);
36555             }
36556             this.fireEvent("paneladded", this, panel);
36557             return panel;
36558         }
36559         */
36560         if(!this.tabs){
36561             this.initTabs();
36562         }else{
36563             this.initPanelAsTab(panel);
36564         }
36565         
36566         
36567         if(panel.background !== true){
36568             this.tabs.activate(panel.getEl().id);
36569         }
36570         this.fireEvent("paneladded", this, panel);
36571         return panel;
36572     },
36573
36574     /**
36575      * Hides the tab for the specified panel.
36576      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36577      */
36578     hidePanel : function(panel){
36579         if(this.tabs && (panel = this.getPanel(panel))){
36580             this.tabs.hideTab(panel.getEl().id);
36581         }
36582     },
36583
36584     /**
36585      * Unhides the tab for a previously hidden panel.
36586      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36587      */
36588     unhidePanel : function(panel){
36589         if(this.tabs && (panel = this.getPanel(panel))){
36590             this.tabs.unhideTab(panel.getEl().id);
36591         }
36592     },
36593
36594     clearPanels : function(){
36595         while(this.panels.getCount() > 0){
36596              this.remove(this.panels.first());
36597         }
36598     },
36599
36600     /**
36601      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36602      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36603      * @param {Boolean} preservePanel Overrides the config preservePanel option
36604      * @return {Roo.ContentPanel} The panel that was removed
36605      */
36606     remove : function(panel, preservePanel)
36607     {
36608         panel = this.getPanel(panel);
36609         if(!panel){
36610             return null;
36611         }
36612         var e = {};
36613         this.fireEvent("beforeremove", this, panel, e);
36614         if(e.cancel === true){
36615             return null;
36616         }
36617         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36618         var panelId = panel.getId();
36619         this.panels.removeKey(panelId);
36620         if(preservePanel){
36621             document.body.appendChild(panel.getEl().dom);
36622         }
36623         if(this.tabs){
36624             this.tabs.removeTab(panel.getEl().id);
36625         }else if (!preservePanel){
36626             this.bodyEl.dom.removeChild(panel.getEl().dom);
36627         }
36628         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36629             var p = this.panels.first();
36630             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36631             tempEl.appendChild(p.getEl().dom);
36632             this.bodyEl.update("");
36633             this.bodyEl.dom.appendChild(p.getEl().dom);
36634             tempEl = null;
36635             this.updateTitle(p.getTitle());
36636             this.tabs = null;
36637             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36638             this.setActivePanel(p);
36639         }
36640         panel.setRegion(null);
36641         if(this.activePanel == panel){
36642             this.activePanel = null;
36643         }
36644         if(this.config.autoDestroy !== false && preservePanel !== true){
36645             try{panel.destroy();}catch(e){}
36646         }
36647         this.fireEvent("panelremoved", this, panel);
36648         return panel;
36649     },
36650
36651     /**
36652      * Returns the TabPanel component used by this region
36653      * @return {Roo.TabPanel}
36654      */
36655     getTabs : function(){
36656         return this.tabs;
36657     },
36658
36659     createTool : function(parentEl, className){
36660         var btn = Roo.DomHelper.append(parentEl, {
36661             tag: "div",
36662             cls: "x-layout-tools-button",
36663             children: [ {
36664                 tag: "div",
36665                 cls: "roo-layout-tools-button-inner " + className,
36666                 html: "&#160;"
36667             }]
36668         }, true);
36669         btn.addClassOnOver("roo-layout-tools-button-over");
36670         return btn;
36671     }
36672 });/*
36673  * Based on:
36674  * Ext JS Library 1.1.1
36675  * Copyright(c) 2006-2007, Ext JS, LLC.
36676  *
36677  * Originally Released Under LGPL - original licence link has changed is not relivant.
36678  *
36679  * Fork - LGPL
36680  * <script type="text/javascript">
36681  */
36682  
36683
36684
36685 /**
36686  * @class Roo.SplitLayoutRegion
36687  * @extends Roo.LayoutRegion
36688  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36689  */
36690 Roo.bootstrap.layout.Split = function(config){
36691     this.cursor = config.cursor;
36692     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36693 };
36694
36695 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36696 {
36697     splitTip : "Drag to resize.",
36698     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36699     useSplitTips : false,
36700
36701     applyConfig : function(config){
36702         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36703     },
36704     
36705     onRender : function(ctr,pos) {
36706         
36707         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36708         if(!this.config.split){
36709             return;
36710         }
36711         if(!this.split){
36712             
36713             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36714                             tag: "div",
36715                             id: this.el.id + "-split",
36716                             cls: "roo-layout-split roo-layout-split-"+this.position,
36717                             html: "&#160;"
36718             });
36719             /** The SplitBar for this region 
36720             * @type Roo.SplitBar */
36721             // does not exist yet...
36722             Roo.log([this.position, this.orientation]);
36723             
36724             this.split = new Roo.bootstrap.SplitBar({
36725                 dragElement : splitEl,
36726                 resizingElement: this.el,
36727                 orientation : this.orientation
36728             });
36729             
36730             this.split.on("moved", this.onSplitMove, this);
36731             this.split.useShim = this.config.useShim === true;
36732             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36733             if(this.useSplitTips){
36734                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36735             }
36736             //if(config.collapsible){
36737             //    this.split.el.on("dblclick", this.collapse,  this);
36738             //}
36739         }
36740         if(typeof this.config.minSize != "undefined"){
36741             this.split.minSize = this.config.minSize;
36742         }
36743         if(typeof this.config.maxSize != "undefined"){
36744             this.split.maxSize = this.config.maxSize;
36745         }
36746         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36747             this.hideSplitter();
36748         }
36749         
36750     },
36751
36752     getHMaxSize : function(){
36753          var cmax = this.config.maxSize || 10000;
36754          var center = this.mgr.getRegion("center");
36755          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36756     },
36757
36758     getVMaxSize : function(){
36759          var cmax = this.config.maxSize || 10000;
36760          var center = this.mgr.getRegion("center");
36761          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36762     },
36763
36764     onSplitMove : function(split, newSize){
36765         this.fireEvent("resized", this, newSize);
36766     },
36767     
36768     /** 
36769      * Returns the {@link Roo.SplitBar} for this region.
36770      * @return {Roo.SplitBar}
36771      */
36772     getSplitBar : function(){
36773         return this.split;
36774     },
36775     
36776     hide : function(){
36777         this.hideSplitter();
36778         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36779     },
36780
36781     hideSplitter : function(){
36782         if(this.split){
36783             this.split.el.setLocation(-2000,-2000);
36784             this.split.el.hide();
36785         }
36786     },
36787
36788     show : function(){
36789         if(this.split){
36790             this.split.el.show();
36791         }
36792         Roo.bootstrap.layout.Split.superclass.show.call(this);
36793     },
36794     
36795     beforeSlide: function(){
36796         if(Roo.isGecko){// firefox overflow auto bug workaround
36797             this.bodyEl.clip();
36798             if(this.tabs) {
36799                 this.tabs.bodyEl.clip();
36800             }
36801             if(this.activePanel){
36802                 this.activePanel.getEl().clip();
36803                 
36804                 if(this.activePanel.beforeSlide){
36805                     this.activePanel.beforeSlide();
36806                 }
36807             }
36808         }
36809     },
36810     
36811     afterSlide : function(){
36812         if(Roo.isGecko){// firefox overflow auto bug workaround
36813             this.bodyEl.unclip();
36814             if(this.tabs) {
36815                 this.tabs.bodyEl.unclip();
36816             }
36817             if(this.activePanel){
36818                 this.activePanel.getEl().unclip();
36819                 if(this.activePanel.afterSlide){
36820                     this.activePanel.afterSlide();
36821                 }
36822             }
36823         }
36824     },
36825
36826     initAutoHide : function(){
36827         if(this.autoHide !== false){
36828             if(!this.autoHideHd){
36829                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36830                 this.autoHideHd = {
36831                     "mouseout": function(e){
36832                         if(!e.within(this.el, true)){
36833                             st.delay(500);
36834                         }
36835                     },
36836                     "mouseover" : function(e){
36837                         st.cancel();
36838                     },
36839                     scope : this
36840                 };
36841             }
36842             this.el.on(this.autoHideHd);
36843         }
36844     },
36845
36846     clearAutoHide : function(){
36847         if(this.autoHide !== false){
36848             this.el.un("mouseout", this.autoHideHd.mouseout);
36849             this.el.un("mouseover", this.autoHideHd.mouseover);
36850         }
36851     },
36852
36853     clearMonitor : function(){
36854         Roo.get(document).un("click", this.slideInIf, this);
36855     },
36856
36857     // these names are backwards but not changed for compat
36858     slideOut : function(){
36859         if(this.isSlid || this.el.hasActiveFx()){
36860             return;
36861         }
36862         this.isSlid = true;
36863         if(this.collapseBtn){
36864             this.collapseBtn.hide();
36865         }
36866         this.closeBtnState = this.closeBtn.getStyle('display');
36867         this.closeBtn.hide();
36868         if(this.stickBtn){
36869             this.stickBtn.show();
36870         }
36871         this.el.show();
36872         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36873         this.beforeSlide();
36874         this.el.setStyle("z-index", 10001);
36875         this.el.slideIn(this.getSlideAnchor(), {
36876             callback: function(){
36877                 this.afterSlide();
36878                 this.initAutoHide();
36879                 Roo.get(document).on("click", this.slideInIf, this);
36880                 this.fireEvent("slideshow", this);
36881             },
36882             scope: this,
36883             block: true
36884         });
36885     },
36886
36887     afterSlideIn : function(){
36888         this.clearAutoHide();
36889         this.isSlid = false;
36890         this.clearMonitor();
36891         this.el.setStyle("z-index", "");
36892         if(this.collapseBtn){
36893             this.collapseBtn.show();
36894         }
36895         this.closeBtn.setStyle('display', this.closeBtnState);
36896         if(this.stickBtn){
36897             this.stickBtn.hide();
36898         }
36899         this.fireEvent("slidehide", this);
36900     },
36901
36902     slideIn : function(cb){
36903         if(!this.isSlid || this.el.hasActiveFx()){
36904             Roo.callback(cb);
36905             return;
36906         }
36907         this.isSlid = false;
36908         this.beforeSlide();
36909         this.el.slideOut(this.getSlideAnchor(), {
36910             callback: function(){
36911                 this.el.setLeftTop(-10000, -10000);
36912                 this.afterSlide();
36913                 this.afterSlideIn();
36914                 Roo.callback(cb);
36915             },
36916             scope: this,
36917             block: true
36918         });
36919     },
36920     
36921     slideInIf : function(e){
36922         if(!e.within(this.el)){
36923             this.slideIn();
36924         }
36925     },
36926
36927     animateCollapse : function(){
36928         this.beforeSlide();
36929         this.el.setStyle("z-index", 20000);
36930         var anchor = this.getSlideAnchor();
36931         this.el.slideOut(anchor, {
36932             callback : function(){
36933                 this.el.setStyle("z-index", "");
36934                 this.collapsedEl.slideIn(anchor, {duration:.3});
36935                 this.afterSlide();
36936                 this.el.setLocation(-10000,-10000);
36937                 this.el.hide();
36938                 this.fireEvent("collapsed", this);
36939             },
36940             scope: this,
36941             block: true
36942         });
36943     },
36944
36945     animateExpand : function(){
36946         this.beforeSlide();
36947         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36948         this.el.setStyle("z-index", 20000);
36949         this.collapsedEl.hide({
36950             duration:.1
36951         });
36952         this.el.slideIn(this.getSlideAnchor(), {
36953             callback : function(){
36954                 this.el.setStyle("z-index", "");
36955                 this.afterSlide();
36956                 if(this.split){
36957                     this.split.el.show();
36958                 }
36959                 this.fireEvent("invalidated", this);
36960                 this.fireEvent("expanded", this);
36961             },
36962             scope: this,
36963             block: true
36964         });
36965     },
36966
36967     anchors : {
36968         "west" : "left",
36969         "east" : "right",
36970         "north" : "top",
36971         "south" : "bottom"
36972     },
36973
36974     sanchors : {
36975         "west" : "l",
36976         "east" : "r",
36977         "north" : "t",
36978         "south" : "b"
36979     },
36980
36981     canchors : {
36982         "west" : "tl-tr",
36983         "east" : "tr-tl",
36984         "north" : "tl-bl",
36985         "south" : "bl-tl"
36986     },
36987
36988     getAnchor : function(){
36989         return this.anchors[this.position];
36990     },
36991
36992     getCollapseAnchor : function(){
36993         return this.canchors[this.position];
36994     },
36995
36996     getSlideAnchor : function(){
36997         return this.sanchors[this.position];
36998     },
36999
37000     getAlignAdj : function(){
37001         var cm = this.cmargins;
37002         switch(this.position){
37003             case "west":
37004                 return [0, 0];
37005             break;
37006             case "east":
37007                 return [0, 0];
37008             break;
37009             case "north":
37010                 return [0, 0];
37011             break;
37012             case "south":
37013                 return [0, 0];
37014             break;
37015         }
37016     },
37017
37018     getExpandAdj : function(){
37019         var c = this.collapsedEl, cm = this.cmargins;
37020         switch(this.position){
37021             case "west":
37022                 return [-(cm.right+c.getWidth()+cm.left), 0];
37023             break;
37024             case "east":
37025                 return [cm.right+c.getWidth()+cm.left, 0];
37026             break;
37027             case "north":
37028                 return [0, -(cm.top+cm.bottom+c.getHeight())];
37029             break;
37030             case "south":
37031                 return [0, cm.top+cm.bottom+c.getHeight()];
37032             break;
37033         }
37034     }
37035 });/*
37036  * Based on:
37037  * Ext JS Library 1.1.1
37038  * Copyright(c) 2006-2007, Ext JS, LLC.
37039  *
37040  * Originally Released Under LGPL - original licence link has changed is not relivant.
37041  *
37042  * Fork - LGPL
37043  * <script type="text/javascript">
37044  */
37045 /*
37046  * These classes are private internal classes
37047  */
37048 Roo.bootstrap.layout.Center = function(config){
37049     config.region = "center";
37050     Roo.bootstrap.layout.Region.call(this, config);
37051     this.visible = true;
37052     this.minWidth = config.minWidth || 20;
37053     this.minHeight = config.minHeight || 20;
37054 };
37055
37056 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37057     hide : function(){
37058         // center panel can't be hidden
37059     },
37060     
37061     show : function(){
37062         // center panel can't be hidden
37063     },
37064     
37065     getMinWidth: function(){
37066         return this.minWidth;
37067     },
37068     
37069     getMinHeight: function(){
37070         return this.minHeight;
37071     }
37072 });
37073
37074
37075
37076
37077  
37078
37079
37080
37081
37082
37083
37084 Roo.bootstrap.layout.North = function(config)
37085 {
37086     config.region = 'north';
37087     config.cursor = 'n-resize';
37088     
37089     Roo.bootstrap.layout.Split.call(this, config);
37090     
37091     
37092     if(this.split){
37093         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37094         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37095         this.split.el.addClass("roo-layout-split-v");
37096     }
37097     var size = config.initialSize || config.height;
37098     if(typeof size != "undefined"){
37099         this.el.setHeight(size);
37100     }
37101 };
37102 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37103 {
37104     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37105     
37106     
37107     
37108     getBox : function(){
37109         if(this.collapsed){
37110             return this.collapsedEl.getBox();
37111         }
37112         var box = this.el.getBox();
37113         if(this.split){
37114             box.height += this.split.el.getHeight();
37115         }
37116         return box;
37117     },
37118     
37119     updateBox : function(box){
37120         if(this.split && !this.collapsed){
37121             box.height -= this.split.el.getHeight();
37122             this.split.el.setLeft(box.x);
37123             this.split.el.setTop(box.y+box.height);
37124             this.split.el.setWidth(box.width);
37125         }
37126         if(this.collapsed){
37127             this.updateBody(box.width, null);
37128         }
37129         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37130     }
37131 });
37132
37133
37134
37135
37136
37137 Roo.bootstrap.layout.South = function(config){
37138     config.region = 'south';
37139     config.cursor = 's-resize';
37140     Roo.bootstrap.layout.Split.call(this, config);
37141     if(this.split){
37142         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37143         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37144         this.split.el.addClass("roo-layout-split-v");
37145     }
37146     var size = config.initialSize || config.height;
37147     if(typeof size != "undefined"){
37148         this.el.setHeight(size);
37149     }
37150 };
37151
37152 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37153     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37154     getBox : function(){
37155         if(this.collapsed){
37156             return this.collapsedEl.getBox();
37157         }
37158         var box = this.el.getBox();
37159         if(this.split){
37160             var sh = this.split.el.getHeight();
37161             box.height += sh;
37162             box.y -= sh;
37163         }
37164         return box;
37165     },
37166     
37167     updateBox : function(box){
37168         if(this.split && !this.collapsed){
37169             var sh = this.split.el.getHeight();
37170             box.height -= sh;
37171             box.y += sh;
37172             this.split.el.setLeft(box.x);
37173             this.split.el.setTop(box.y-sh);
37174             this.split.el.setWidth(box.width);
37175         }
37176         if(this.collapsed){
37177             this.updateBody(box.width, null);
37178         }
37179         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37180     }
37181 });
37182
37183 Roo.bootstrap.layout.East = function(config){
37184     config.region = "east";
37185     config.cursor = "e-resize";
37186     Roo.bootstrap.layout.Split.call(this, config);
37187     if(this.split){
37188         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37189         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37190         this.split.el.addClass("roo-layout-split-h");
37191     }
37192     var size = config.initialSize || config.width;
37193     if(typeof size != "undefined"){
37194         this.el.setWidth(size);
37195     }
37196 };
37197 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37198     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37199     getBox : function(){
37200         if(this.collapsed){
37201             return this.collapsedEl.getBox();
37202         }
37203         var box = this.el.getBox();
37204         if(this.split){
37205             var sw = this.split.el.getWidth();
37206             box.width += sw;
37207             box.x -= sw;
37208         }
37209         return box;
37210     },
37211
37212     updateBox : function(box){
37213         if(this.split && !this.collapsed){
37214             var sw = this.split.el.getWidth();
37215             box.width -= sw;
37216             this.split.el.setLeft(box.x);
37217             this.split.el.setTop(box.y);
37218             this.split.el.setHeight(box.height);
37219             box.x += sw;
37220         }
37221         if(this.collapsed){
37222             this.updateBody(null, box.height);
37223         }
37224         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37225     }
37226 });
37227
37228 Roo.bootstrap.layout.West = function(config){
37229     config.region = "west";
37230     config.cursor = "w-resize";
37231     
37232     Roo.bootstrap.layout.Split.call(this, config);
37233     if(this.split){
37234         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37235         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37236         this.split.el.addClass("roo-layout-split-h");
37237     }
37238     
37239 };
37240 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37241     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37242     
37243     onRender: function(ctr, pos)
37244     {
37245         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37246         var size = this.config.initialSize || this.config.width;
37247         if(typeof size != "undefined"){
37248             this.el.setWidth(size);
37249         }
37250     },
37251     
37252     getBox : function(){
37253         if(this.collapsed){
37254             return this.collapsedEl.getBox();
37255         }
37256         var box = this.el.getBox();
37257         if(this.split){
37258             box.width += this.split.el.getWidth();
37259         }
37260         return box;
37261     },
37262     
37263     updateBox : function(box){
37264         if(this.split && !this.collapsed){
37265             var sw = this.split.el.getWidth();
37266             box.width -= sw;
37267             this.split.el.setLeft(box.x+box.width);
37268             this.split.el.setTop(box.y);
37269             this.split.el.setHeight(box.height);
37270         }
37271         if(this.collapsed){
37272             this.updateBody(null, box.height);
37273         }
37274         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37275     }
37276 });Roo.namespace("Roo.bootstrap.panel");/*
37277  * Based on:
37278  * Ext JS Library 1.1.1
37279  * Copyright(c) 2006-2007, Ext JS, LLC.
37280  *
37281  * Originally Released Under LGPL - original licence link has changed is not relivant.
37282  *
37283  * Fork - LGPL
37284  * <script type="text/javascript">
37285  */
37286 /**
37287  * @class Roo.ContentPanel
37288  * @extends Roo.util.Observable
37289  * A basic ContentPanel element.
37290  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37291  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37292  * @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
37293  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37294  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37295  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37296  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37297  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37298  * @cfg {String} title          The title for this panel
37299  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37300  * @cfg {String} url            Calls {@link #setUrl} with this value
37301  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37302  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37303  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37304  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37305  * @cfg {Boolean} badges render the badges
37306
37307  * @constructor
37308  * Create a new ContentPanel.
37309  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37310  * @param {String/Object} config A string to set only the title or a config object
37311  * @param {String} content (optional) Set the HTML content for this panel
37312  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37313  */
37314 Roo.bootstrap.panel.Content = function( config){
37315     
37316     this.tpl = config.tpl || false;
37317     
37318     var el = config.el;
37319     var content = config.content;
37320
37321     if(config.autoCreate){ // xtype is available if this is called from factory
37322         el = Roo.id();
37323     }
37324     this.el = Roo.get(el);
37325     if(!this.el && config && config.autoCreate){
37326         if(typeof config.autoCreate == "object"){
37327             if(!config.autoCreate.id){
37328                 config.autoCreate.id = config.id||el;
37329             }
37330             this.el = Roo.DomHelper.append(document.body,
37331                         config.autoCreate, true);
37332         }else{
37333             var elcfg =  {   tag: "div",
37334                             cls: "roo-layout-inactive-content",
37335                             id: config.id||el
37336                             };
37337             if (config.html) {
37338                 elcfg.html = config.html;
37339                 
37340             }
37341                         
37342             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37343         }
37344     } 
37345     this.closable = false;
37346     this.loaded = false;
37347     this.active = false;
37348    
37349       
37350     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37351         
37352         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37353         
37354         this.wrapEl = this.el; //this.el.wrap();
37355         var ti = [];
37356         if (config.toolbar.items) {
37357             ti = config.toolbar.items ;
37358             delete config.toolbar.items ;
37359         }
37360         
37361         var nitems = [];
37362         this.toolbar.render(this.wrapEl, 'before');
37363         for(var i =0;i < ti.length;i++) {
37364           //  Roo.log(['add child', items[i]]);
37365             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37366         }
37367         this.toolbar.items = nitems;
37368         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37369         delete config.toolbar;
37370         
37371     }
37372     /*
37373     // xtype created footer. - not sure if will work as we normally have to render first..
37374     if (this.footer && !this.footer.el && this.footer.xtype) {
37375         if (!this.wrapEl) {
37376             this.wrapEl = this.el.wrap();
37377         }
37378     
37379         this.footer.container = this.wrapEl.createChild();
37380          
37381         this.footer = Roo.factory(this.footer, Roo);
37382         
37383     }
37384     */
37385     
37386      if(typeof config == "string"){
37387         this.title = config;
37388     }else{
37389         Roo.apply(this, config);
37390     }
37391     
37392     if(this.resizeEl){
37393         this.resizeEl = Roo.get(this.resizeEl, true);
37394     }else{
37395         this.resizeEl = this.el;
37396     }
37397     // handle view.xtype
37398     
37399  
37400     
37401     
37402     this.addEvents({
37403         /**
37404          * @event activate
37405          * Fires when this panel is activated. 
37406          * @param {Roo.ContentPanel} this
37407          */
37408         "activate" : true,
37409         /**
37410          * @event deactivate
37411          * Fires when this panel is activated. 
37412          * @param {Roo.ContentPanel} this
37413          */
37414         "deactivate" : true,
37415
37416         /**
37417          * @event resize
37418          * Fires when this panel is resized if fitToFrame is true.
37419          * @param {Roo.ContentPanel} this
37420          * @param {Number} width The width after any component adjustments
37421          * @param {Number} height The height after any component adjustments
37422          */
37423         "resize" : true,
37424         
37425          /**
37426          * @event render
37427          * Fires when this tab is created
37428          * @param {Roo.ContentPanel} this
37429          */
37430         "render" : true
37431         
37432         
37433         
37434     });
37435     
37436
37437     
37438     
37439     if(this.autoScroll){
37440         this.resizeEl.setStyle("overflow", "auto");
37441     } else {
37442         // fix randome scrolling
37443         //this.el.on('scroll', function() {
37444         //    Roo.log('fix random scolling');
37445         //    this.scrollTo('top',0); 
37446         //});
37447     }
37448     content = content || this.content;
37449     if(content){
37450         this.setContent(content);
37451     }
37452     if(config && config.url){
37453         this.setUrl(this.url, this.params, this.loadOnce);
37454     }
37455     
37456     
37457     
37458     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37459     
37460     if (this.view && typeof(this.view.xtype) != 'undefined') {
37461         this.view.el = this.el.appendChild(document.createElement("div"));
37462         this.view = Roo.factory(this.view); 
37463         this.view.render  &&  this.view.render(false, '');  
37464     }
37465     
37466     
37467     this.fireEvent('render', this);
37468 };
37469
37470 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37471     
37472     tabTip : '',
37473     
37474     setRegion : function(region){
37475         this.region = region;
37476         this.setActiveClass(region && !this.background);
37477     },
37478     
37479     
37480     setActiveClass: function(state)
37481     {
37482         if(state){
37483            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37484            this.el.setStyle('position','relative');
37485         }else{
37486            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37487            this.el.setStyle('position', 'absolute');
37488         } 
37489     },
37490     
37491     /**
37492      * Returns the toolbar for this Panel if one was configured. 
37493      * @return {Roo.Toolbar} 
37494      */
37495     getToolbar : function(){
37496         return this.toolbar;
37497     },
37498     
37499     setActiveState : function(active)
37500     {
37501         this.active = active;
37502         this.setActiveClass(active);
37503         if(!active){
37504             if(this.fireEvent("deactivate", this) === false){
37505                 return false;
37506             }
37507             return true;
37508         }
37509         this.fireEvent("activate", this);
37510         return true;
37511     },
37512     /**
37513      * Updates this panel's element
37514      * @param {String} content The new content
37515      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37516     */
37517     setContent : function(content, loadScripts){
37518         this.el.update(content, loadScripts);
37519     },
37520
37521     ignoreResize : function(w, h){
37522         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37523             return true;
37524         }else{
37525             this.lastSize = {width: w, height: h};
37526             return false;
37527         }
37528     },
37529     /**
37530      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37531      * @return {Roo.UpdateManager} The UpdateManager
37532      */
37533     getUpdateManager : function(){
37534         return this.el.getUpdateManager();
37535     },
37536      /**
37537      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37538      * @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:
37539 <pre><code>
37540 panel.load({
37541     url: "your-url.php",
37542     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37543     callback: yourFunction,
37544     scope: yourObject, //(optional scope)
37545     discardUrl: false,
37546     nocache: false,
37547     text: "Loading...",
37548     timeout: 30,
37549     scripts: false
37550 });
37551 </code></pre>
37552      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37553      * 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.
37554      * @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}
37555      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37556      * @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.
37557      * @return {Roo.ContentPanel} this
37558      */
37559     load : function(){
37560         var um = this.el.getUpdateManager();
37561         um.update.apply(um, arguments);
37562         return this;
37563     },
37564
37565
37566     /**
37567      * 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.
37568      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37569      * @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)
37570      * @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)
37571      * @return {Roo.UpdateManager} The UpdateManager
37572      */
37573     setUrl : function(url, params, loadOnce){
37574         if(this.refreshDelegate){
37575             this.removeListener("activate", this.refreshDelegate);
37576         }
37577         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37578         this.on("activate", this.refreshDelegate);
37579         return this.el.getUpdateManager();
37580     },
37581     
37582     _handleRefresh : function(url, params, loadOnce){
37583         if(!loadOnce || !this.loaded){
37584             var updater = this.el.getUpdateManager();
37585             updater.update(url, params, this._setLoaded.createDelegate(this));
37586         }
37587     },
37588     
37589     _setLoaded : function(){
37590         this.loaded = true;
37591     }, 
37592     
37593     /**
37594      * Returns this panel's id
37595      * @return {String} 
37596      */
37597     getId : function(){
37598         return this.el.id;
37599     },
37600     
37601     /** 
37602      * Returns this panel's element - used by regiosn to add.
37603      * @return {Roo.Element} 
37604      */
37605     getEl : function(){
37606         return this.wrapEl || this.el;
37607     },
37608     
37609    
37610     
37611     adjustForComponents : function(width, height)
37612     {
37613         //Roo.log('adjustForComponents ');
37614         if(this.resizeEl != this.el){
37615             width -= this.el.getFrameWidth('lr');
37616             height -= this.el.getFrameWidth('tb');
37617         }
37618         if(this.toolbar){
37619             var te = this.toolbar.getEl();
37620             te.setWidth(width);
37621             height -= te.getHeight();
37622         }
37623         if(this.footer){
37624             var te = this.footer.getEl();
37625             te.setWidth(width);
37626             height -= te.getHeight();
37627         }
37628         
37629         
37630         if(this.adjustments){
37631             width += this.adjustments[0];
37632             height += this.adjustments[1];
37633         }
37634         return {"width": width, "height": height};
37635     },
37636     
37637     setSize : function(width, height){
37638         if(this.fitToFrame && !this.ignoreResize(width, height)){
37639             if(this.fitContainer && this.resizeEl != this.el){
37640                 this.el.setSize(width, height);
37641             }
37642             var size = this.adjustForComponents(width, height);
37643             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37644             this.fireEvent('resize', this, size.width, size.height);
37645         }
37646     },
37647     
37648     /**
37649      * Returns this panel's title
37650      * @return {String} 
37651      */
37652     getTitle : function(){
37653         
37654         if (typeof(this.title) != 'object') {
37655             return this.title;
37656         }
37657         
37658         var t = '';
37659         for (var k in this.title) {
37660             if (!this.title.hasOwnProperty(k)) {
37661                 continue;
37662             }
37663             
37664             if (k.indexOf('-') >= 0) {
37665                 var s = k.split('-');
37666                 for (var i = 0; i<s.length; i++) {
37667                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37668                 }
37669             } else {
37670                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37671             }
37672         }
37673         return t;
37674     },
37675     
37676     /**
37677      * Set this panel's title
37678      * @param {String} title
37679      */
37680     setTitle : function(title){
37681         this.title = title;
37682         if(this.region){
37683             this.region.updatePanelTitle(this, title);
37684         }
37685     },
37686     
37687     /**
37688      * Returns true is this panel was configured to be closable
37689      * @return {Boolean} 
37690      */
37691     isClosable : function(){
37692         return this.closable;
37693     },
37694     
37695     beforeSlide : function(){
37696         this.el.clip();
37697         this.resizeEl.clip();
37698     },
37699     
37700     afterSlide : function(){
37701         this.el.unclip();
37702         this.resizeEl.unclip();
37703     },
37704     
37705     /**
37706      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37707      *   Will fail silently if the {@link #setUrl} method has not been called.
37708      *   This does not activate the panel, just updates its content.
37709      */
37710     refresh : function(){
37711         if(this.refreshDelegate){
37712            this.loaded = false;
37713            this.refreshDelegate();
37714         }
37715     },
37716     
37717     /**
37718      * Destroys this panel
37719      */
37720     destroy : function(){
37721         this.el.removeAllListeners();
37722         var tempEl = document.createElement("span");
37723         tempEl.appendChild(this.el.dom);
37724         tempEl.innerHTML = "";
37725         this.el.remove();
37726         this.el = null;
37727     },
37728     
37729     /**
37730      * form - if the content panel contains a form - this is a reference to it.
37731      * @type {Roo.form.Form}
37732      */
37733     form : false,
37734     /**
37735      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37736      *    This contains a reference to it.
37737      * @type {Roo.View}
37738      */
37739     view : false,
37740     
37741       /**
37742      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37743      * <pre><code>
37744
37745 layout.addxtype({
37746        xtype : 'Form',
37747        items: [ .... ]
37748    }
37749 );
37750
37751 </code></pre>
37752      * @param {Object} cfg Xtype definition of item to add.
37753      */
37754     
37755     
37756     getChildContainer: function () {
37757         return this.getEl();
37758     }
37759     
37760     
37761     /*
37762         var  ret = new Roo.factory(cfg);
37763         return ret;
37764         
37765         
37766         // add form..
37767         if (cfg.xtype.match(/^Form$/)) {
37768             
37769             var el;
37770             //if (this.footer) {
37771             //    el = this.footer.container.insertSibling(false, 'before');
37772             //} else {
37773                 el = this.el.createChild();
37774             //}
37775
37776             this.form = new  Roo.form.Form(cfg);
37777             
37778             
37779             if ( this.form.allItems.length) {
37780                 this.form.render(el.dom);
37781             }
37782             return this.form;
37783         }
37784         // should only have one of theses..
37785         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37786             // views.. should not be just added - used named prop 'view''
37787             
37788             cfg.el = this.el.appendChild(document.createElement("div"));
37789             // factory?
37790             
37791             var ret = new Roo.factory(cfg);
37792              
37793              ret.render && ret.render(false, ''); // render blank..
37794             this.view = ret;
37795             return ret;
37796         }
37797         return false;
37798     }
37799     \*/
37800 });
37801  
37802 /**
37803  * @class Roo.bootstrap.panel.Grid
37804  * @extends Roo.bootstrap.panel.Content
37805  * @constructor
37806  * Create a new GridPanel.
37807  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37808  * @param {Object} config A the config object
37809   
37810  */
37811
37812
37813
37814 Roo.bootstrap.panel.Grid = function(config)
37815 {
37816     
37817       
37818     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37819         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37820
37821     config.el = this.wrapper;
37822     //this.el = this.wrapper;
37823     
37824       if (config.container) {
37825         // ctor'ed from a Border/panel.grid
37826         
37827         
37828         this.wrapper.setStyle("overflow", "hidden");
37829         this.wrapper.addClass('roo-grid-container');
37830
37831     }
37832     
37833     
37834     if(config.toolbar){
37835         var tool_el = this.wrapper.createChild();    
37836         this.toolbar = Roo.factory(config.toolbar);
37837         var ti = [];
37838         if (config.toolbar.items) {
37839             ti = config.toolbar.items ;
37840             delete config.toolbar.items ;
37841         }
37842         
37843         var nitems = [];
37844         this.toolbar.render(tool_el);
37845         for(var i =0;i < ti.length;i++) {
37846           //  Roo.log(['add child', items[i]]);
37847             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37848         }
37849         this.toolbar.items = nitems;
37850         
37851         delete config.toolbar;
37852     }
37853     
37854     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37855     config.grid.scrollBody = true;;
37856     config.grid.monitorWindowResize = false; // turn off autosizing
37857     config.grid.autoHeight = false;
37858     config.grid.autoWidth = false;
37859     
37860     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37861     
37862     if (config.background) {
37863         // render grid on panel activation (if panel background)
37864         this.on('activate', function(gp) {
37865             if (!gp.grid.rendered) {
37866                 gp.grid.render(this.wrapper);
37867                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37868             }
37869         });
37870             
37871     } else {
37872         this.grid.render(this.wrapper);
37873         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37874
37875     }
37876     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37877     // ??? needed ??? config.el = this.wrapper;
37878     
37879     
37880     
37881   
37882     // xtype created footer. - not sure if will work as we normally have to render first..
37883     if (this.footer && !this.footer.el && this.footer.xtype) {
37884         
37885         var ctr = this.grid.getView().getFooterPanel(true);
37886         this.footer.dataSource = this.grid.dataSource;
37887         this.footer = Roo.factory(this.footer, Roo);
37888         this.footer.render(ctr);
37889         
37890     }
37891     
37892     
37893     
37894     
37895      
37896 };
37897
37898 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37899     getId : function(){
37900         return this.grid.id;
37901     },
37902     
37903     /**
37904      * Returns the grid for this panel
37905      * @return {Roo.bootstrap.Table} 
37906      */
37907     getGrid : function(){
37908         return this.grid;    
37909     },
37910     
37911     setSize : function(width, height){
37912         if(!this.ignoreResize(width, height)){
37913             var grid = this.grid;
37914             var size = this.adjustForComponents(width, height);
37915             var gridel = grid.getGridEl();
37916             gridel.setSize(size.width, size.height);
37917             /*
37918             var thd = grid.getGridEl().select('thead',true).first();
37919             var tbd = grid.getGridEl().select('tbody', true).first();
37920             if (tbd) {
37921                 tbd.setSize(width, height - thd.getHeight());
37922             }
37923             */
37924             grid.autoSize();
37925         }
37926     },
37927      
37928     
37929     
37930     beforeSlide : function(){
37931         this.grid.getView().scroller.clip();
37932     },
37933     
37934     afterSlide : function(){
37935         this.grid.getView().scroller.unclip();
37936     },
37937     
37938     destroy : function(){
37939         this.grid.destroy();
37940         delete this.grid;
37941         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37942     }
37943 });
37944
37945 /**
37946  * @class Roo.bootstrap.panel.Nest
37947  * @extends Roo.bootstrap.panel.Content
37948  * @constructor
37949  * Create a new Panel, that can contain a layout.Border.
37950  * 
37951  * 
37952  * @param {Roo.BorderLayout} layout The layout for this panel
37953  * @param {String/Object} config A string to set only the title or a config object
37954  */
37955 Roo.bootstrap.panel.Nest = function(config)
37956 {
37957     // construct with only one argument..
37958     /* FIXME - implement nicer consturctors
37959     if (layout.layout) {
37960         config = layout;
37961         layout = config.layout;
37962         delete config.layout;
37963     }
37964     if (layout.xtype && !layout.getEl) {
37965         // then layout needs constructing..
37966         layout = Roo.factory(layout, Roo);
37967     }
37968     */
37969     
37970     config.el =  config.layout.getEl();
37971     
37972     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37973     
37974     config.layout.monitorWindowResize = false; // turn off autosizing
37975     this.layout = config.layout;
37976     this.layout.getEl().addClass("roo-layout-nested-layout");
37977     this.layout.parent = this;
37978     
37979     
37980     
37981     
37982 };
37983
37984 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37985
37986     setSize : function(width, height){
37987         if(!this.ignoreResize(width, height)){
37988             var size = this.adjustForComponents(width, height);
37989             var el = this.layout.getEl();
37990             if (size.height < 1) {
37991                 el.setWidth(size.width);   
37992             } else {
37993                 el.setSize(size.width, size.height);
37994             }
37995             var touch = el.dom.offsetWidth;
37996             this.layout.layout();
37997             // ie requires a double layout on the first pass
37998             if(Roo.isIE && !this.initialized){
37999                 this.initialized = true;
38000                 this.layout.layout();
38001             }
38002         }
38003     },
38004     
38005     // activate all subpanels if not currently active..
38006     
38007     setActiveState : function(active){
38008         this.active = active;
38009         this.setActiveClass(active);
38010         
38011         if(!active){
38012             this.fireEvent("deactivate", this);
38013             return;
38014         }
38015         
38016         this.fireEvent("activate", this);
38017         // not sure if this should happen before or after..
38018         if (!this.layout) {
38019             return; // should not happen..
38020         }
38021         var reg = false;
38022         for (var r in this.layout.regions) {
38023             reg = this.layout.getRegion(r);
38024             if (reg.getActivePanel()) {
38025                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
38026                 reg.setActivePanel(reg.getActivePanel());
38027                 continue;
38028             }
38029             if (!reg.panels.length) {
38030                 continue;
38031             }
38032             reg.showPanel(reg.getPanel(0));
38033         }
38034         
38035         
38036         
38037         
38038     },
38039     
38040     /**
38041      * Returns the nested BorderLayout for this panel
38042      * @return {Roo.BorderLayout} 
38043      */
38044     getLayout : function(){
38045         return this.layout;
38046     },
38047     
38048      /**
38049      * Adds a xtype elements to the layout of the nested panel
38050      * <pre><code>
38051
38052 panel.addxtype({
38053        xtype : 'ContentPanel',
38054        region: 'west',
38055        items: [ .... ]
38056    }
38057 );
38058
38059 panel.addxtype({
38060         xtype : 'NestedLayoutPanel',
38061         region: 'west',
38062         layout: {
38063            center: { },
38064            west: { }   
38065         },
38066         items : [ ... list of content panels or nested layout panels.. ]
38067    }
38068 );
38069 </code></pre>
38070      * @param {Object} cfg Xtype definition of item to add.
38071      */
38072     addxtype : function(cfg) {
38073         return this.layout.addxtype(cfg);
38074     
38075     }
38076 });/*
38077  * Based on:
38078  * Ext JS Library 1.1.1
38079  * Copyright(c) 2006-2007, Ext JS, LLC.
38080  *
38081  * Originally Released Under LGPL - original licence link has changed is not relivant.
38082  *
38083  * Fork - LGPL
38084  * <script type="text/javascript">
38085  */
38086 /**
38087  * @class Roo.TabPanel
38088  * @extends Roo.util.Observable
38089  * A lightweight tab container.
38090  * <br><br>
38091  * Usage:
38092  * <pre><code>
38093 // basic tabs 1, built from existing content
38094 var tabs = new Roo.TabPanel("tabs1");
38095 tabs.addTab("script", "View Script");
38096 tabs.addTab("markup", "View Markup");
38097 tabs.activate("script");
38098
38099 // more advanced tabs, built from javascript
38100 var jtabs = new Roo.TabPanel("jtabs");
38101 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38102
38103 // set up the UpdateManager
38104 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38105 var updater = tab2.getUpdateManager();
38106 updater.setDefaultUrl("ajax1.htm");
38107 tab2.on('activate', updater.refresh, updater, true);
38108
38109 // Use setUrl for Ajax loading
38110 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38111 tab3.setUrl("ajax2.htm", null, true);
38112
38113 // Disabled tab
38114 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38115 tab4.disable();
38116
38117 jtabs.activate("jtabs-1");
38118  * </code></pre>
38119  * @constructor
38120  * Create a new TabPanel.
38121  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38122  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38123  */
38124 Roo.bootstrap.panel.Tabs = function(config){
38125     /**
38126     * The container element for this TabPanel.
38127     * @type Roo.Element
38128     */
38129     this.el = Roo.get(config.el);
38130     delete config.el;
38131     if(config){
38132         if(typeof config == "boolean"){
38133             this.tabPosition = config ? "bottom" : "top";
38134         }else{
38135             Roo.apply(this, config);
38136         }
38137     }
38138     
38139     if(this.tabPosition == "bottom"){
38140         // if tabs are at the bottom = create the body first.
38141         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38142         this.el.addClass("roo-tabs-bottom");
38143     }
38144     // next create the tabs holders
38145     
38146     if (this.tabPosition == "west"){
38147         
38148         var reg = this.region; // fake it..
38149         while (reg) {
38150             if (!reg.mgr.parent) {
38151                 break;
38152             }
38153             reg = reg.mgr.parent.region;
38154         }
38155         Roo.log("got nest?");
38156         Roo.log(reg);
38157         if (reg.mgr.getRegion('west')) {
38158             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38159             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38160             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38161             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38162             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38163         
38164             
38165         }
38166         
38167         
38168     } else {
38169      
38170         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38171         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38172         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38173         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38174     }
38175     
38176     
38177     if(Roo.isIE){
38178         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38179     }
38180     
38181     // finally - if tabs are at the top, then create the body last..
38182     if(this.tabPosition != "bottom"){
38183         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38184          * @type Roo.Element
38185          */
38186         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38187         this.el.addClass("roo-tabs-top");
38188     }
38189     this.items = [];
38190
38191     this.bodyEl.setStyle("position", "relative");
38192
38193     this.active = null;
38194     this.activateDelegate = this.activate.createDelegate(this);
38195
38196     this.addEvents({
38197         /**
38198          * @event tabchange
38199          * Fires when the active tab changes
38200          * @param {Roo.TabPanel} this
38201          * @param {Roo.TabPanelItem} activePanel The new active tab
38202          */
38203         "tabchange": true,
38204         /**
38205          * @event beforetabchange
38206          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38207          * @param {Roo.TabPanel} this
38208          * @param {Object} e Set cancel to true on this object to cancel the tab change
38209          * @param {Roo.TabPanelItem} tab The tab being changed to
38210          */
38211         "beforetabchange" : true
38212     });
38213
38214     Roo.EventManager.onWindowResize(this.onResize, this);
38215     this.cpad = this.el.getPadding("lr");
38216     this.hiddenCount = 0;
38217
38218
38219     // toolbar on the tabbar support...
38220     if (this.toolbar) {
38221         alert("no toolbar support yet");
38222         this.toolbar  = false;
38223         /*
38224         var tcfg = this.toolbar;
38225         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38226         this.toolbar = new Roo.Toolbar(tcfg);
38227         if (Roo.isSafari) {
38228             var tbl = tcfg.container.child('table', true);
38229             tbl.setAttribute('width', '100%');
38230         }
38231         */
38232         
38233     }
38234    
38235
38236
38237     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38238 };
38239
38240 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38241     /*
38242      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38243      */
38244     tabPosition : "top",
38245     /*
38246      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38247      */
38248     currentTabWidth : 0,
38249     /*
38250      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38251      */
38252     minTabWidth : 40,
38253     /*
38254      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38255      */
38256     maxTabWidth : 250,
38257     /*
38258      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38259      */
38260     preferredTabWidth : 175,
38261     /*
38262      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38263      */
38264     resizeTabs : false,
38265     /*
38266      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38267      */
38268     monitorResize : true,
38269     /*
38270      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38271      */
38272     toolbar : false,  // set by caller..
38273     
38274     region : false, /// set by caller
38275     
38276     disableTooltips : true, // not used yet...
38277
38278     /**
38279      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38280      * @param {String} id The id of the div to use <b>or create</b>
38281      * @param {String} text The text for the tab
38282      * @param {String} content (optional) Content to put in the TabPanelItem body
38283      * @param {Boolean} closable (optional) True to create a close icon on the tab
38284      * @return {Roo.TabPanelItem} The created TabPanelItem
38285      */
38286     addTab : function(id, text, content, closable, tpl)
38287     {
38288         var item = new Roo.bootstrap.panel.TabItem({
38289             panel: this,
38290             id : id,
38291             text : text,
38292             closable : closable,
38293             tpl : tpl
38294         });
38295         this.addTabItem(item);
38296         if(content){
38297             item.setContent(content);
38298         }
38299         return item;
38300     },
38301
38302     /**
38303      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38304      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38305      * @return {Roo.TabPanelItem}
38306      */
38307     getTab : function(id){
38308         return this.items[id];
38309     },
38310
38311     /**
38312      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38313      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38314      */
38315     hideTab : function(id){
38316         var t = this.items[id];
38317         if(!t.isHidden()){
38318            t.setHidden(true);
38319            this.hiddenCount++;
38320            this.autoSizeTabs();
38321         }
38322     },
38323
38324     /**
38325      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38326      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38327      */
38328     unhideTab : function(id){
38329         var t = this.items[id];
38330         if(t.isHidden()){
38331            t.setHidden(false);
38332            this.hiddenCount--;
38333            this.autoSizeTabs();
38334         }
38335     },
38336
38337     /**
38338      * Adds an existing {@link Roo.TabPanelItem}.
38339      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38340      */
38341     addTabItem : function(item)
38342     {
38343         this.items[item.id] = item;
38344         this.items.push(item);
38345         this.autoSizeTabs();
38346       //  if(this.resizeTabs){
38347     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38348   //         this.autoSizeTabs();
38349 //        }else{
38350 //            item.autoSize();
38351        // }
38352     },
38353
38354     /**
38355      * Removes a {@link Roo.TabPanelItem}.
38356      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38357      */
38358     removeTab : function(id){
38359         var items = this.items;
38360         var tab = items[id];
38361         if(!tab) { return; }
38362         var index = items.indexOf(tab);
38363         if(this.active == tab && items.length > 1){
38364             var newTab = this.getNextAvailable(index);
38365             if(newTab) {
38366                 newTab.activate();
38367             }
38368         }
38369         this.stripEl.dom.removeChild(tab.pnode.dom);
38370         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38371             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38372         }
38373         items.splice(index, 1);
38374         delete this.items[tab.id];
38375         tab.fireEvent("close", tab);
38376         tab.purgeListeners();
38377         this.autoSizeTabs();
38378     },
38379
38380     getNextAvailable : function(start){
38381         var items = this.items;
38382         var index = start;
38383         // look for a next tab that will slide over to
38384         // replace the one being removed
38385         while(index < items.length){
38386             var item = items[++index];
38387             if(item && !item.isHidden()){
38388                 return item;
38389             }
38390         }
38391         // if one isn't found select the previous tab (on the left)
38392         index = start;
38393         while(index >= 0){
38394             var item = items[--index];
38395             if(item && !item.isHidden()){
38396                 return item;
38397             }
38398         }
38399         return null;
38400     },
38401
38402     /**
38403      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38404      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38405      */
38406     disableTab : function(id){
38407         var tab = this.items[id];
38408         if(tab && this.active != tab){
38409             tab.disable();
38410         }
38411     },
38412
38413     /**
38414      * Enables a {@link Roo.TabPanelItem} that is disabled.
38415      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38416      */
38417     enableTab : function(id){
38418         var tab = this.items[id];
38419         tab.enable();
38420     },
38421
38422     /**
38423      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38424      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38425      * @return {Roo.TabPanelItem} The TabPanelItem.
38426      */
38427     activate : function(id)
38428     {
38429         //Roo.log('activite:'  + id);
38430         
38431         var tab = this.items[id];
38432         if(!tab){
38433             return null;
38434         }
38435         if(tab == this.active || tab.disabled){
38436             return tab;
38437         }
38438         var e = {};
38439         this.fireEvent("beforetabchange", this, e, tab);
38440         if(e.cancel !== true && !tab.disabled){
38441             if(this.active){
38442                 this.active.hide();
38443             }
38444             this.active = this.items[id];
38445             this.active.show();
38446             this.fireEvent("tabchange", this, this.active);
38447         }
38448         return tab;
38449     },
38450
38451     /**
38452      * Gets the active {@link Roo.TabPanelItem}.
38453      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38454      */
38455     getActiveTab : function(){
38456         return this.active;
38457     },
38458
38459     /**
38460      * Updates the tab body element to fit the height of the container element
38461      * for overflow scrolling
38462      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38463      */
38464     syncHeight : function(targetHeight){
38465         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38466         var bm = this.bodyEl.getMargins();
38467         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38468         this.bodyEl.setHeight(newHeight);
38469         return newHeight;
38470     },
38471
38472     onResize : function(){
38473         if(this.monitorResize){
38474             this.autoSizeTabs();
38475         }
38476     },
38477
38478     /**
38479      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38480      */
38481     beginUpdate : function(){
38482         this.updating = true;
38483     },
38484
38485     /**
38486      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38487      */
38488     endUpdate : function(){
38489         this.updating = false;
38490         this.autoSizeTabs();
38491     },
38492
38493     /**
38494      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38495      */
38496     autoSizeTabs : function()
38497     {
38498         var count = this.items.length;
38499         var vcount = count - this.hiddenCount;
38500         
38501         if (vcount < 2) {
38502             this.stripEl.hide();
38503         } else {
38504             this.stripEl.show();
38505         }
38506         
38507         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38508             return;
38509         }
38510         
38511         
38512         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38513         var availWidth = Math.floor(w / vcount);
38514         var b = this.stripBody;
38515         if(b.getWidth() > w){
38516             var tabs = this.items;
38517             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38518             if(availWidth < this.minTabWidth){
38519                 /*if(!this.sleft){    // incomplete scrolling code
38520                     this.createScrollButtons();
38521                 }
38522                 this.showScroll();
38523                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38524             }
38525         }else{
38526             if(this.currentTabWidth < this.preferredTabWidth){
38527                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38528             }
38529         }
38530     },
38531
38532     /**
38533      * Returns the number of tabs in this TabPanel.
38534      * @return {Number}
38535      */
38536      getCount : function(){
38537          return this.items.length;
38538      },
38539
38540     /**
38541      * Resizes all the tabs to the passed width
38542      * @param {Number} The new width
38543      */
38544     setTabWidth : function(width){
38545         this.currentTabWidth = width;
38546         for(var i = 0, len = this.items.length; i < len; i++) {
38547                 if(!this.items[i].isHidden()) {
38548                 this.items[i].setWidth(width);
38549             }
38550         }
38551     },
38552
38553     /**
38554      * Destroys this TabPanel
38555      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38556      */
38557     destroy : function(removeEl){
38558         Roo.EventManager.removeResizeListener(this.onResize, this);
38559         for(var i = 0, len = this.items.length; i < len; i++){
38560             this.items[i].purgeListeners();
38561         }
38562         if(removeEl === true){
38563             this.el.update("");
38564             this.el.remove();
38565         }
38566     },
38567     
38568     createStrip : function(container)
38569     {
38570         var strip = document.createElement("nav");
38571         strip.className = Roo.bootstrap.version == 4 ?
38572             "navbar-light bg-light" : 
38573             "navbar navbar-default"; //"x-tabs-wrap";
38574         container.appendChild(strip);
38575         return strip;
38576     },
38577     
38578     createStripList : function(strip)
38579     {
38580         // div wrapper for retard IE
38581         // returns the "tr" element.
38582         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38583         //'<div class="x-tabs-strip-wrap">'+
38584           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38585           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38586         return strip.firstChild; //.firstChild.firstChild.firstChild;
38587     },
38588     createBody : function(container)
38589     {
38590         var body = document.createElement("div");
38591         Roo.id(body, "tab-body");
38592         //Roo.fly(body).addClass("x-tabs-body");
38593         Roo.fly(body).addClass("tab-content");
38594         container.appendChild(body);
38595         return body;
38596     },
38597     createItemBody :function(bodyEl, id){
38598         var body = Roo.getDom(id);
38599         if(!body){
38600             body = document.createElement("div");
38601             body.id = id;
38602         }
38603         //Roo.fly(body).addClass("x-tabs-item-body");
38604         Roo.fly(body).addClass("tab-pane");
38605          bodyEl.insertBefore(body, bodyEl.firstChild);
38606         return body;
38607     },
38608     /** @private */
38609     createStripElements :  function(stripEl, text, closable, tpl)
38610     {
38611         var td = document.createElement("li"); // was td..
38612         td.className = 'nav-item';
38613         
38614         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38615         
38616         
38617         stripEl.appendChild(td);
38618         /*if(closable){
38619             td.className = "x-tabs-closable";
38620             if(!this.closeTpl){
38621                 this.closeTpl = new Roo.Template(
38622                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38623                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38624                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38625                 );
38626             }
38627             var el = this.closeTpl.overwrite(td, {"text": text});
38628             var close = el.getElementsByTagName("div")[0];
38629             var inner = el.getElementsByTagName("em")[0];
38630             return {"el": el, "close": close, "inner": inner};
38631         } else {
38632         */
38633         // not sure what this is..
38634 //            if(!this.tabTpl){
38635                 //this.tabTpl = new Roo.Template(
38636                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38637                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38638                 //);
38639 //                this.tabTpl = new Roo.Template(
38640 //                   '<a href="#">' +
38641 //                   '<span unselectable="on"' +
38642 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38643 //                            ' >{text}</span></a>'
38644 //                );
38645 //                
38646 //            }
38647
38648
38649             var template = tpl || this.tabTpl || false;
38650             
38651             if(!template){
38652                 template =  new Roo.Template(
38653                         Roo.bootstrap.version == 4 ? 
38654                             (
38655                                 '<a class="nav-link" href="#" unselectable="on"' +
38656                                      (this.disableTooltips ? '' : ' title="{text}"') +
38657                                      ' >{text}</a>'
38658                             ) : (
38659                                 '<a class="nav-link" href="#">' +
38660                                 '<span unselectable="on"' +
38661                                          (this.disableTooltips ? '' : ' title="{text}"') +
38662                                     ' >{text}</span></a>'
38663                             )
38664                 );
38665             }
38666             
38667             switch (typeof(template)) {
38668                 case 'object' :
38669                     break;
38670                 case 'string' :
38671                     template = new Roo.Template(template);
38672                     break;
38673                 default :
38674                     break;
38675             }
38676             
38677             var el = template.overwrite(td, {"text": text});
38678             
38679             var inner = el.getElementsByTagName("span")[0];
38680             
38681             return {"el": el, "inner": inner};
38682             
38683     }
38684         
38685     
38686 });
38687
38688 /**
38689  * @class Roo.TabPanelItem
38690  * @extends Roo.util.Observable
38691  * Represents an individual item (tab plus body) in a TabPanel.
38692  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38693  * @param {String} id The id of this TabPanelItem
38694  * @param {String} text The text for the tab of this TabPanelItem
38695  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38696  */
38697 Roo.bootstrap.panel.TabItem = function(config){
38698     /**
38699      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38700      * @type Roo.TabPanel
38701      */
38702     this.tabPanel = config.panel;
38703     /**
38704      * The id for this TabPanelItem
38705      * @type String
38706      */
38707     this.id = config.id;
38708     /** @private */
38709     this.disabled = false;
38710     /** @private */
38711     this.text = config.text;
38712     /** @private */
38713     this.loaded = false;
38714     this.closable = config.closable;
38715
38716     /**
38717      * The body element for this TabPanelItem.
38718      * @type Roo.Element
38719      */
38720     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38721     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38722     this.bodyEl.setStyle("display", "block");
38723     this.bodyEl.setStyle("zoom", "1");
38724     //this.hideAction();
38725
38726     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38727     /** @private */
38728     this.el = Roo.get(els.el);
38729     this.inner = Roo.get(els.inner, true);
38730      this.textEl = Roo.bootstrap.version == 4 ?
38731         this.el : Roo.get(this.el.dom.firstChild, true);
38732
38733     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38734     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38735
38736     
38737 //    this.el.on("mousedown", this.onTabMouseDown, this);
38738     this.el.on("click", this.onTabClick, this);
38739     /** @private */
38740     if(config.closable){
38741         var c = Roo.get(els.close, true);
38742         c.dom.title = this.closeText;
38743         c.addClassOnOver("close-over");
38744         c.on("click", this.closeClick, this);
38745      }
38746
38747     this.addEvents({
38748          /**
38749          * @event activate
38750          * Fires when this tab becomes the active tab.
38751          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38752          * @param {Roo.TabPanelItem} this
38753          */
38754         "activate": true,
38755         /**
38756          * @event beforeclose
38757          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38758          * @param {Roo.TabPanelItem} this
38759          * @param {Object} e Set cancel to true on this object to cancel the close.
38760          */
38761         "beforeclose": true,
38762         /**
38763          * @event close
38764          * Fires when this tab is closed.
38765          * @param {Roo.TabPanelItem} this
38766          */
38767          "close": true,
38768         /**
38769          * @event deactivate
38770          * Fires when this tab is no longer the active tab.
38771          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38772          * @param {Roo.TabPanelItem} this
38773          */
38774          "deactivate" : true
38775     });
38776     this.hidden = false;
38777
38778     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38779 };
38780
38781 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38782            {
38783     purgeListeners : function(){
38784        Roo.util.Observable.prototype.purgeListeners.call(this);
38785        this.el.removeAllListeners();
38786     },
38787     /**
38788      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38789      */
38790     show : function(){
38791         this.status_node.addClass("active");
38792         this.showAction();
38793         if(Roo.isOpera){
38794             this.tabPanel.stripWrap.repaint();
38795         }
38796         this.fireEvent("activate", this.tabPanel, this);
38797     },
38798
38799     /**
38800      * Returns true if this tab is the active tab.
38801      * @return {Boolean}
38802      */
38803     isActive : function(){
38804         return this.tabPanel.getActiveTab() == this;
38805     },
38806
38807     /**
38808      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38809      */
38810     hide : function(){
38811         this.status_node.removeClass("active");
38812         this.hideAction();
38813         this.fireEvent("deactivate", this.tabPanel, this);
38814     },
38815
38816     hideAction : function(){
38817         this.bodyEl.hide();
38818         this.bodyEl.setStyle("position", "absolute");
38819         this.bodyEl.setLeft("-20000px");
38820         this.bodyEl.setTop("-20000px");
38821     },
38822
38823     showAction : function(){
38824         this.bodyEl.setStyle("position", "relative");
38825         this.bodyEl.setTop("");
38826         this.bodyEl.setLeft("");
38827         this.bodyEl.show();
38828     },
38829
38830     /**
38831      * Set the tooltip for the tab.
38832      * @param {String} tooltip The tab's tooltip
38833      */
38834     setTooltip : function(text){
38835         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38836             this.textEl.dom.qtip = text;
38837             this.textEl.dom.removeAttribute('title');
38838         }else{
38839             this.textEl.dom.title = text;
38840         }
38841     },
38842
38843     onTabClick : function(e){
38844         e.preventDefault();
38845         this.tabPanel.activate(this.id);
38846     },
38847
38848     onTabMouseDown : function(e){
38849         e.preventDefault();
38850         this.tabPanel.activate(this.id);
38851     },
38852 /*
38853     getWidth : function(){
38854         return this.inner.getWidth();
38855     },
38856
38857     setWidth : function(width){
38858         var iwidth = width - this.linode.getPadding("lr");
38859         this.inner.setWidth(iwidth);
38860         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38861         this.linode.setWidth(width);
38862     },
38863 */
38864     /**
38865      * Show or hide the tab
38866      * @param {Boolean} hidden True to hide or false to show.
38867      */
38868     setHidden : function(hidden){
38869         this.hidden = hidden;
38870         this.linode.setStyle("display", hidden ? "none" : "");
38871     },
38872
38873     /**
38874      * Returns true if this tab is "hidden"
38875      * @return {Boolean}
38876      */
38877     isHidden : function(){
38878         return this.hidden;
38879     },
38880
38881     /**
38882      * Returns the text for this tab
38883      * @return {String}
38884      */
38885     getText : function(){
38886         return this.text;
38887     },
38888     /*
38889     autoSize : function(){
38890         //this.el.beginMeasure();
38891         this.textEl.setWidth(1);
38892         /*
38893          *  #2804 [new] Tabs in Roojs
38894          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38895          */
38896         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38897         //this.el.endMeasure();
38898     //},
38899
38900     /**
38901      * Sets the text for the tab (Note: this also sets the tooltip text)
38902      * @param {String} text The tab's text and tooltip
38903      */
38904     setText : function(text){
38905         this.text = text;
38906         this.textEl.update(text);
38907         this.setTooltip(text);
38908         //if(!this.tabPanel.resizeTabs){
38909         //    this.autoSize();
38910         //}
38911     },
38912     /**
38913      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38914      */
38915     activate : function(){
38916         this.tabPanel.activate(this.id);
38917     },
38918
38919     /**
38920      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38921      */
38922     disable : function(){
38923         if(this.tabPanel.active != this){
38924             this.disabled = true;
38925             this.status_node.addClass("disabled");
38926         }
38927     },
38928
38929     /**
38930      * Enables this TabPanelItem if it was previously disabled.
38931      */
38932     enable : function(){
38933         this.disabled = false;
38934         this.status_node.removeClass("disabled");
38935     },
38936
38937     /**
38938      * Sets the content for this TabPanelItem.
38939      * @param {String} content The content
38940      * @param {Boolean} loadScripts true to look for and load scripts
38941      */
38942     setContent : function(content, loadScripts){
38943         this.bodyEl.update(content, loadScripts);
38944     },
38945
38946     /**
38947      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38948      * @return {Roo.UpdateManager} The UpdateManager
38949      */
38950     getUpdateManager : function(){
38951         return this.bodyEl.getUpdateManager();
38952     },
38953
38954     /**
38955      * Set a URL to be used to load the content for this TabPanelItem.
38956      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38957      * @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)
38958      * @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)
38959      * @return {Roo.UpdateManager} The UpdateManager
38960      */
38961     setUrl : function(url, params, loadOnce){
38962         if(this.refreshDelegate){
38963             this.un('activate', this.refreshDelegate);
38964         }
38965         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38966         this.on("activate", this.refreshDelegate);
38967         return this.bodyEl.getUpdateManager();
38968     },
38969
38970     /** @private */
38971     _handleRefresh : function(url, params, loadOnce){
38972         if(!loadOnce || !this.loaded){
38973             var updater = this.bodyEl.getUpdateManager();
38974             updater.update(url, params, this._setLoaded.createDelegate(this));
38975         }
38976     },
38977
38978     /**
38979      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38980      *   Will fail silently if the setUrl method has not been called.
38981      *   This does not activate the panel, just updates its content.
38982      */
38983     refresh : function(){
38984         if(this.refreshDelegate){
38985            this.loaded = false;
38986            this.refreshDelegate();
38987         }
38988     },
38989
38990     /** @private */
38991     _setLoaded : function(){
38992         this.loaded = true;
38993     },
38994
38995     /** @private */
38996     closeClick : function(e){
38997         var o = {};
38998         e.stopEvent();
38999         this.fireEvent("beforeclose", this, o);
39000         if(o.cancel !== true){
39001             this.tabPanel.removeTab(this.id);
39002         }
39003     },
39004     /**
39005      * The text displayed in the tooltip for the close icon.
39006      * @type String
39007      */
39008     closeText : "Close this tab"
39009 });
39010 /**
39011 *    This script refer to:
39012 *    Title: International Telephone Input
39013 *    Author: Jack O'Connor
39014 *    Code version:  v12.1.12
39015 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39016 **/
39017
39018 Roo.bootstrap.PhoneInputData = function() {
39019     var d = [
39020       [
39021         "Afghanistan (‫افغانستان‬‎)",
39022         "af",
39023         "93"
39024       ],
39025       [
39026         "Albania (Shqipëri)",
39027         "al",
39028         "355"
39029       ],
39030       [
39031         "Algeria (‫الجزائر‬‎)",
39032         "dz",
39033         "213"
39034       ],
39035       [
39036         "American Samoa",
39037         "as",
39038         "1684"
39039       ],
39040       [
39041         "Andorra",
39042         "ad",
39043         "376"
39044       ],
39045       [
39046         "Angola",
39047         "ao",
39048         "244"
39049       ],
39050       [
39051         "Anguilla",
39052         "ai",
39053         "1264"
39054       ],
39055       [
39056         "Antigua and Barbuda",
39057         "ag",
39058         "1268"
39059       ],
39060       [
39061         "Argentina",
39062         "ar",
39063         "54"
39064       ],
39065       [
39066         "Armenia (Հայաստան)",
39067         "am",
39068         "374"
39069       ],
39070       [
39071         "Aruba",
39072         "aw",
39073         "297"
39074       ],
39075       [
39076         "Australia",
39077         "au",
39078         "61",
39079         0
39080       ],
39081       [
39082         "Austria (Österreich)",
39083         "at",
39084         "43"
39085       ],
39086       [
39087         "Azerbaijan (Azərbaycan)",
39088         "az",
39089         "994"
39090       ],
39091       [
39092         "Bahamas",
39093         "bs",
39094         "1242"
39095       ],
39096       [
39097         "Bahrain (‫البحرين‬‎)",
39098         "bh",
39099         "973"
39100       ],
39101       [
39102         "Bangladesh (বাংলাদেশ)",
39103         "bd",
39104         "880"
39105       ],
39106       [
39107         "Barbados",
39108         "bb",
39109         "1246"
39110       ],
39111       [
39112         "Belarus (Беларусь)",
39113         "by",
39114         "375"
39115       ],
39116       [
39117         "Belgium (België)",
39118         "be",
39119         "32"
39120       ],
39121       [
39122         "Belize",
39123         "bz",
39124         "501"
39125       ],
39126       [
39127         "Benin (Bénin)",
39128         "bj",
39129         "229"
39130       ],
39131       [
39132         "Bermuda",
39133         "bm",
39134         "1441"
39135       ],
39136       [
39137         "Bhutan (འབྲུག)",
39138         "bt",
39139         "975"
39140       ],
39141       [
39142         "Bolivia",
39143         "bo",
39144         "591"
39145       ],
39146       [
39147         "Bosnia and Herzegovina (Босна и Херцеговина)",
39148         "ba",
39149         "387"
39150       ],
39151       [
39152         "Botswana",
39153         "bw",
39154         "267"
39155       ],
39156       [
39157         "Brazil (Brasil)",
39158         "br",
39159         "55"
39160       ],
39161       [
39162         "British Indian Ocean Territory",
39163         "io",
39164         "246"
39165       ],
39166       [
39167         "British Virgin Islands",
39168         "vg",
39169         "1284"
39170       ],
39171       [
39172         "Brunei",
39173         "bn",
39174         "673"
39175       ],
39176       [
39177         "Bulgaria (България)",
39178         "bg",
39179         "359"
39180       ],
39181       [
39182         "Burkina Faso",
39183         "bf",
39184         "226"
39185       ],
39186       [
39187         "Burundi (Uburundi)",
39188         "bi",
39189         "257"
39190       ],
39191       [
39192         "Cambodia (កម្ពុជា)",
39193         "kh",
39194         "855"
39195       ],
39196       [
39197         "Cameroon (Cameroun)",
39198         "cm",
39199         "237"
39200       ],
39201       [
39202         "Canada",
39203         "ca",
39204         "1",
39205         1,
39206         ["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"]
39207       ],
39208       [
39209         "Cape Verde (Kabu Verdi)",
39210         "cv",
39211         "238"
39212       ],
39213       [
39214         "Caribbean Netherlands",
39215         "bq",
39216         "599",
39217         1
39218       ],
39219       [
39220         "Cayman Islands",
39221         "ky",
39222         "1345"
39223       ],
39224       [
39225         "Central African Republic (République centrafricaine)",
39226         "cf",
39227         "236"
39228       ],
39229       [
39230         "Chad (Tchad)",
39231         "td",
39232         "235"
39233       ],
39234       [
39235         "Chile",
39236         "cl",
39237         "56"
39238       ],
39239       [
39240         "China (中国)",
39241         "cn",
39242         "86"
39243       ],
39244       [
39245         "Christmas Island",
39246         "cx",
39247         "61",
39248         2
39249       ],
39250       [
39251         "Cocos (Keeling) Islands",
39252         "cc",
39253         "61",
39254         1
39255       ],
39256       [
39257         "Colombia",
39258         "co",
39259         "57"
39260       ],
39261       [
39262         "Comoros (‫جزر القمر‬‎)",
39263         "km",
39264         "269"
39265       ],
39266       [
39267         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39268         "cd",
39269         "243"
39270       ],
39271       [
39272         "Congo (Republic) (Congo-Brazzaville)",
39273         "cg",
39274         "242"
39275       ],
39276       [
39277         "Cook Islands",
39278         "ck",
39279         "682"
39280       ],
39281       [
39282         "Costa Rica",
39283         "cr",
39284         "506"
39285       ],
39286       [
39287         "Côte d’Ivoire",
39288         "ci",
39289         "225"
39290       ],
39291       [
39292         "Croatia (Hrvatska)",
39293         "hr",
39294         "385"
39295       ],
39296       [
39297         "Cuba",
39298         "cu",
39299         "53"
39300       ],
39301       [
39302         "Curaçao",
39303         "cw",
39304         "599",
39305         0
39306       ],
39307       [
39308         "Cyprus (Κύπρος)",
39309         "cy",
39310         "357"
39311       ],
39312       [
39313         "Czech Republic (Česká republika)",
39314         "cz",
39315         "420"
39316       ],
39317       [
39318         "Denmark (Danmark)",
39319         "dk",
39320         "45"
39321       ],
39322       [
39323         "Djibouti",
39324         "dj",
39325         "253"
39326       ],
39327       [
39328         "Dominica",
39329         "dm",
39330         "1767"
39331       ],
39332       [
39333         "Dominican Republic (República Dominicana)",
39334         "do",
39335         "1",
39336         2,
39337         ["809", "829", "849"]
39338       ],
39339       [
39340         "Ecuador",
39341         "ec",
39342         "593"
39343       ],
39344       [
39345         "Egypt (‫مصر‬‎)",
39346         "eg",
39347         "20"
39348       ],
39349       [
39350         "El Salvador",
39351         "sv",
39352         "503"
39353       ],
39354       [
39355         "Equatorial Guinea (Guinea Ecuatorial)",
39356         "gq",
39357         "240"
39358       ],
39359       [
39360         "Eritrea",
39361         "er",
39362         "291"
39363       ],
39364       [
39365         "Estonia (Eesti)",
39366         "ee",
39367         "372"
39368       ],
39369       [
39370         "Ethiopia",
39371         "et",
39372         "251"
39373       ],
39374       [
39375         "Falkland Islands (Islas Malvinas)",
39376         "fk",
39377         "500"
39378       ],
39379       [
39380         "Faroe Islands (Føroyar)",
39381         "fo",
39382         "298"
39383       ],
39384       [
39385         "Fiji",
39386         "fj",
39387         "679"
39388       ],
39389       [
39390         "Finland (Suomi)",
39391         "fi",
39392         "358",
39393         0
39394       ],
39395       [
39396         "France",
39397         "fr",
39398         "33"
39399       ],
39400       [
39401         "French Guiana (Guyane française)",
39402         "gf",
39403         "594"
39404       ],
39405       [
39406         "French Polynesia (Polynésie française)",
39407         "pf",
39408         "689"
39409       ],
39410       [
39411         "Gabon",
39412         "ga",
39413         "241"
39414       ],
39415       [
39416         "Gambia",
39417         "gm",
39418         "220"
39419       ],
39420       [
39421         "Georgia (საქართველო)",
39422         "ge",
39423         "995"
39424       ],
39425       [
39426         "Germany (Deutschland)",
39427         "de",
39428         "49"
39429       ],
39430       [
39431         "Ghana (Gaana)",
39432         "gh",
39433         "233"
39434       ],
39435       [
39436         "Gibraltar",
39437         "gi",
39438         "350"
39439       ],
39440       [
39441         "Greece (Ελλάδα)",
39442         "gr",
39443         "30"
39444       ],
39445       [
39446         "Greenland (Kalaallit Nunaat)",
39447         "gl",
39448         "299"
39449       ],
39450       [
39451         "Grenada",
39452         "gd",
39453         "1473"
39454       ],
39455       [
39456         "Guadeloupe",
39457         "gp",
39458         "590",
39459         0
39460       ],
39461       [
39462         "Guam",
39463         "gu",
39464         "1671"
39465       ],
39466       [
39467         "Guatemala",
39468         "gt",
39469         "502"
39470       ],
39471       [
39472         "Guernsey",
39473         "gg",
39474         "44",
39475         1
39476       ],
39477       [
39478         "Guinea (Guinée)",
39479         "gn",
39480         "224"
39481       ],
39482       [
39483         "Guinea-Bissau (Guiné Bissau)",
39484         "gw",
39485         "245"
39486       ],
39487       [
39488         "Guyana",
39489         "gy",
39490         "592"
39491       ],
39492       [
39493         "Haiti",
39494         "ht",
39495         "509"
39496       ],
39497       [
39498         "Honduras",
39499         "hn",
39500         "504"
39501       ],
39502       [
39503         "Hong Kong (香港)",
39504         "hk",
39505         "852"
39506       ],
39507       [
39508         "Hungary (Magyarország)",
39509         "hu",
39510         "36"
39511       ],
39512       [
39513         "Iceland (Ísland)",
39514         "is",
39515         "354"
39516       ],
39517       [
39518         "India (भारत)",
39519         "in",
39520         "91"
39521       ],
39522       [
39523         "Indonesia",
39524         "id",
39525         "62"
39526       ],
39527       [
39528         "Iran (‫ایران‬‎)",
39529         "ir",
39530         "98"
39531       ],
39532       [
39533         "Iraq (‫العراق‬‎)",
39534         "iq",
39535         "964"
39536       ],
39537       [
39538         "Ireland",
39539         "ie",
39540         "353"
39541       ],
39542       [
39543         "Isle of Man",
39544         "im",
39545         "44",
39546         2
39547       ],
39548       [
39549         "Israel (‫ישראל‬‎)",
39550         "il",
39551         "972"
39552       ],
39553       [
39554         "Italy (Italia)",
39555         "it",
39556         "39",
39557         0
39558       ],
39559       [
39560         "Jamaica",
39561         "jm",
39562         "1876"
39563       ],
39564       [
39565         "Japan (日本)",
39566         "jp",
39567         "81"
39568       ],
39569       [
39570         "Jersey",
39571         "je",
39572         "44",
39573         3
39574       ],
39575       [
39576         "Jordan (‫الأردن‬‎)",
39577         "jo",
39578         "962"
39579       ],
39580       [
39581         "Kazakhstan (Казахстан)",
39582         "kz",
39583         "7",
39584         1
39585       ],
39586       [
39587         "Kenya",
39588         "ke",
39589         "254"
39590       ],
39591       [
39592         "Kiribati",
39593         "ki",
39594         "686"
39595       ],
39596       [
39597         "Kosovo",
39598         "xk",
39599         "383"
39600       ],
39601       [
39602         "Kuwait (‫الكويت‬‎)",
39603         "kw",
39604         "965"
39605       ],
39606       [
39607         "Kyrgyzstan (Кыргызстан)",
39608         "kg",
39609         "996"
39610       ],
39611       [
39612         "Laos (ລາວ)",
39613         "la",
39614         "856"
39615       ],
39616       [
39617         "Latvia (Latvija)",
39618         "lv",
39619         "371"
39620       ],
39621       [
39622         "Lebanon (‫لبنان‬‎)",
39623         "lb",
39624         "961"
39625       ],
39626       [
39627         "Lesotho",
39628         "ls",
39629         "266"
39630       ],
39631       [
39632         "Liberia",
39633         "lr",
39634         "231"
39635       ],
39636       [
39637         "Libya (‫ليبيا‬‎)",
39638         "ly",
39639         "218"
39640       ],
39641       [
39642         "Liechtenstein",
39643         "li",
39644         "423"
39645       ],
39646       [
39647         "Lithuania (Lietuva)",
39648         "lt",
39649         "370"
39650       ],
39651       [
39652         "Luxembourg",
39653         "lu",
39654         "352"
39655       ],
39656       [
39657         "Macau (澳門)",
39658         "mo",
39659         "853"
39660       ],
39661       [
39662         "Macedonia (FYROM) (Македонија)",
39663         "mk",
39664         "389"
39665       ],
39666       [
39667         "Madagascar (Madagasikara)",
39668         "mg",
39669         "261"
39670       ],
39671       [
39672         "Malawi",
39673         "mw",
39674         "265"
39675       ],
39676       [
39677         "Malaysia",
39678         "my",
39679         "60"
39680       ],
39681       [
39682         "Maldives",
39683         "mv",
39684         "960"
39685       ],
39686       [
39687         "Mali",
39688         "ml",
39689         "223"
39690       ],
39691       [
39692         "Malta",
39693         "mt",
39694         "356"
39695       ],
39696       [
39697         "Marshall Islands",
39698         "mh",
39699         "692"
39700       ],
39701       [
39702         "Martinique",
39703         "mq",
39704         "596"
39705       ],
39706       [
39707         "Mauritania (‫موريتانيا‬‎)",
39708         "mr",
39709         "222"
39710       ],
39711       [
39712         "Mauritius (Moris)",
39713         "mu",
39714         "230"
39715       ],
39716       [
39717         "Mayotte",
39718         "yt",
39719         "262",
39720         1
39721       ],
39722       [
39723         "Mexico (México)",
39724         "mx",
39725         "52"
39726       ],
39727       [
39728         "Micronesia",
39729         "fm",
39730         "691"
39731       ],
39732       [
39733         "Moldova (Republica Moldova)",
39734         "md",
39735         "373"
39736       ],
39737       [
39738         "Monaco",
39739         "mc",
39740         "377"
39741       ],
39742       [
39743         "Mongolia (Монгол)",
39744         "mn",
39745         "976"
39746       ],
39747       [
39748         "Montenegro (Crna Gora)",
39749         "me",
39750         "382"
39751       ],
39752       [
39753         "Montserrat",
39754         "ms",
39755         "1664"
39756       ],
39757       [
39758         "Morocco (‫المغرب‬‎)",
39759         "ma",
39760         "212",
39761         0
39762       ],
39763       [
39764         "Mozambique (Moçambique)",
39765         "mz",
39766         "258"
39767       ],
39768       [
39769         "Myanmar (Burma) (မြန်မာ)",
39770         "mm",
39771         "95"
39772       ],
39773       [
39774         "Namibia (Namibië)",
39775         "na",
39776         "264"
39777       ],
39778       [
39779         "Nauru",
39780         "nr",
39781         "674"
39782       ],
39783       [
39784         "Nepal (नेपाल)",
39785         "np",
39786         "977"
39787       ],
39788       [
39789         "Netherlands (Nederland)",
39790         "nl",
39791         "31"
39792       ],
39793       [
39794         "New Caledonia (Nouvelle-Calédonie)",
39795         "nc",
39796         "687"
39797       ],
39798       [
39799         "New Zealand",
39800         "nz",
39801         "64"
39802       ],
39803       [
39804         "Nicaragua",
39805         "ni",
39806         "505"
39807       ],
39808       [
39809         "Niger (Nijar)",
39810         "ne",
39811         "227"
39812       ],
39813       [
39814         "Nigeria",
39815         "ng",
39816         "234"
39817       ],
39818       [
39819         "Niue",
39820         "nu",
39821         "683"
39822       ],
39823       [
39824         "Norfolk Island",
39825         "nf",
39826         "672"
39827       ],
39828       [
39829         "North Korea (조선 민주주의 인민 공화국)",
39830         "kp",
39831         "850"
39832       ],
39833       [
39834         "Northern Mariana Islands",
39835         "mp",
39836         "1670"
39837       ],
39838       [
39839         "Norway (Norge)",
39840         "no",
39841         "47",
39842         0
39843       ],
39844       [
39845         "Oman (‫عُمان‬‎)",
39846         "om",
39847         "968"
39848       ],
39849       [
39850         "Pakistan (‫پاکستان‬‎)",
39851         "pk",
39852         "92"
39853       ],
39854       [
39855         "Palau",
39856         "pw",
39857         "680"
39858       ],
39859       [
39860         "Palestine (‫فلسطين‬‎)",
39861         "ps",
39862         "970"
39863       ],
39864       [
39865         "Panama (Panamá)",
39866         "pa",
39867         "507"
39868       ],
39869       [
39870         "Papua New Guinea",
39871         "pg",
39872         "675"
39873       ],
39874       [
39875         "Paraguay",
39876         "py",
39877         "595"
39878       ],
39879       [
39880         "Peru (Perú)",
39881         "pe",
39882         "51"
39883       ],
39884       [
39885         "Philippines",
39886         "ph",
39887         "63"
39888       ],
39889       [
39890         "Poland (Polska)",
39891         "pl",
39892         "48"
39893       ],
39894       [
39895         "Portugal",
39896         "pt",
39897         "351"
39898       ],
39899       [
39900         "Puerto Rico",
39901         "pr",
39902         "1",
39903         3,
39904         ["787", "939"]
39905       ],
39906       [
39907         "Qatar (‫قطر‬‎)",
39908         "qa",
39909         "974"
39910       ],
39911       [
39912         "Réunion (La Réunion)",
39913         "re",
39914         "262",
39915         0
39916       ],
39917       [
39918         "Romania (România)",
39919         "ro",
39920         "40"
39921       ],
39922       [
39923         "Russia (Россия)",
39924         "ru",
39925         "7",
39926         0
39927       ],
39928       [
39929         "Rwanda",
39930         "rw",
39931         "250"
39932       ],
39933       [
39934         "Saint Barthélemy",
39935         "bl",
39936         "590",
39937         1
39938       ],
39939       [
39940         "Saint Helena",
39941         "sh",
39942         "290"
39943       ],
39944       [
39945         "Saint Kitts and Nevis",
39946         "kn",
39947         "1869"
39948       ],
39949       [
39950         "Saint Lucia",
39951         "lc",
39952         "1758"
39953       ],
39954       [
39955         "Saint Martin (Saint-Martin (partie française))",
39956         "mf",
39957         "590",
39958         2
39959       ],
39960       [
39961         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39962         "pm",
39963         "508"
39964       ],
39965       [
39966         "Saint Vincent and the Grenadines",
39967         "vc",
39968         "1784"
39969       ],
39970       [
39971         "Samoa",
39972         "ws",
39973         "685"
39974       ],
39975       [
39976         "San Marino",
39977         "sm",
39978         "378"
39979       ],
39980       [
39981         "São Tomé and Príncipe (São Tomé e Príncipe)",
39982         "st",
39983         "239"
39984       ],
39985       [
39986         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39987         "sa",
39988         "966"
39989       ],
39990       [
39991         "Senegal (Sénégal)",
39992         "sn",
39993         "221"
39994       ],
39995       [
39996         "Serbia (Србија)",
39997         "rs",
39998         "381"
39999       ],
40000       [
40001         "Seychelles",
40002         "sc",
40003         "248"
40004       ],
40005       [
40006         "Sierra Leone",
40007         "sl",
40008         "232"
40009       ],
40010       [
40011         "Singapore",
40012         "sg",
40013         "65"
40014       ],
40015       [
40016         "Sint Maarten",
40017         "sx",
40018         "1721"
40019       ],
40020       [
40021         "Slovakia (Slovensko)",
40022         "sk",
40023         "421"
40024       ],
40025       [
40026         "Slovenia (Slovenija)",
40027         "si",
40028         "386"
40029       ],
40030       [
40031         "Solomon Islands",
40032         "sb",
40033         "677"
40034       ],
40035       [
40036         "Somalia (Soomaaliya)",
40037         "so",
40038         "252"
40039       ],
40040       [
40041         "South Africa",
40042         "za",
40043         "27"
40044       ],
40045       [
40046         "South Korea (대한민국)",
40047         "kr",
40048         "82"
40049       ],
40050       [
40051         "South Sudan (‫جنوب السودان‬‎)",
40052         "ss",
40053         "211"
40054       ],
40055       [
40056         "Spain (España)",
40057         "es",
40058         "34"
40059       ],
40060       [
40061         "Sri Lanka (ශ්‍රී ලංකාව)",
40062         "lk",
40063         "94"
40064       ],
40065       [
40066         "Sudan (‫السودان‬‎)",
40067         "sd",
40068         "249"
40069       ],
40070       [
40071         "Suriname",
40072         "sr",
40073         "597"
40074       ],
40075       [
40076         "Svalbard and Jan Mayen",
40077         "sj",
40078         "47",
40079         1
40080       ],
40081       [
40082         "Swaziland",
40083         "sz",
40084         "268"
40085       ],
40086       [
40087         "Sweden (Sverige)",
40088         "se",
40089         "46"
40090       ],
40091       [
40092         "Switzerland (Schweiz)",
40093         "ch",
40094         "41"
40095       ],
40096       [
40097         "Syria (‫سوريا‬‎)",
40098         "sy",
40099         "963"
40100       ],
40101       [
40102         "Taiwan (台灣)",
40103         "tw",
40104         "886"
40105       ],
40106       [
40107         "Tajikistan",
40108         "tj",
40109         "992"
40110       ],
40111       [
40112         "Tanzania",
40113         "tz",
40114         "255"
40115       ],
40116       [
40117         "Thailand (ไทย)",
40118         "th",
40119         "66"
40120       ],
40121       [
40122         "Timor-Leste",
40123         "tl",
40124         "670"
40125       ],
40126       [
40127         "Togo",
40128         "tg",
40129         "228"
40130       ],
40131       [
40132         "Tokelau",
40133         "tk",
40134         "690"
40135       ],
40136       [
40137         "Tonga",
40138         "to",
40139         "676"
40140       ],
40141       [
40142         "Trinidad and Tobago",
40143         "tt",
40144         "1868"
40145       ],
40146       [
40147         "Tunisia (‫تونس‬‎)",
40148         "tn",
40149         "216"
40150       ],
40151       [
40152         "Turkey (Türkiye)",
40153         "tr",
40154         "90"
40155       ],
40156       [
40157         "Turkmenistan",
40158         "tm",
40159         "993"
40160       ],
40161       [
40162         "Turks and Caicos Islands",
40163         "tc",
40164         "1649"
40165       ],
40166       [
40167         "Tuvalu",
40168         "tv",
40169         "688"
40170       ],
40171       [
40172         "U.S. Virgin Islands",
40173         "vi",
40174         "1340"
40175       ],
40176       [
40177         "Uganda",
40178         "ug",
40179         "256"
40180       ],
40181       [
40182         "Ukraine (Україна)",
40183         "ua",
40184         "380"
40185       ],
40186       [
40187         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40188         "ae",
40189         "971"
40190       ],
40191       [
40192         "United Kingdom",
40193         "gb",
40194         "44",
40195         0
40196       ],
40197       [
40198         "United States",
40199         "us",
40200         "1",
40201         0
40202       ],
40203       [
40204         "Uruguay",
40205         "uy",
40206         "598"
40207       ],
40208       [
40209         "Uzbekistan (Oʻzbekiston)",
40210         "uz",
40211         "998"
40212       ],
40213       [
40214         "Vanuatu",
40215         "vu",
40216         "678"
40217       ],
40218       [
40219         "Vatican City (Città del Vaticano)",
40220         "va",
40221         "39",
40222         1
40223       ],
40224       [
40225         "Venezuela",
40226         "ve",
40227         "58"
40228       ],
40229       [
40230         "Vietnam (Việt Nam)",
40231         "vn",
40232         "84"
40233       ],
40234       [
40235         "Wallis and Futuna (Wallis-et-Futuna)",
40236         "wf",
40237         "681"
40238       ],
40239       [
40240         "Western Sahara (‫الصحراء الغربية‬‎)",
40241         "eh",
40242         "212",
40243         1
40244       ],
40245       [
40246         "Yemen (‫اليمن‬‎)",
40247         "ye",
40248         "967"
40249       ],
40250       [
40251         "Zambia",
40252         "zm",
40253         "260"
40254       ],
40255       [
40256         "Zimbabwe",
40257         "zw",
40258         "263"
40259       ],
40260       [
40261         "Åland Islands",
40262         "ax",
40263         "358",
40264         1
40265       ]
40266   ];
40267   
40268   return d;
40269 }/**
40270 *    This script refer to:
40271 *    Title: International Telephone Input
40272 *    Author: Jack O'Connor
40273 *    Code version:  v12.1.12
40274 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40275 **/
40276
40277 /**
40278  * @class Roo.bootstrap.PhoneInput
40279  * @extends Roo.bootstrap.TriggerField
40280  * An input with International dial-code selection
40281  
40282  * @cfg {String} defaultDialCode default '+852'
40283  * @cfg {Array} preferedCountries default []
40284   
40285  * @constructor
40286  * Create a new PhoneInput.
40287  * @param {Object} config Configuration options
40288  */
40289
40290 Roo.bootstrap.PhoneInput = function(config) {
40291     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40292 };
40293
40294 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40295         
40296         listWidth: undefined,
40297         
40298         selectedClass: 'active',
40299         
40300         invalidClass : "has-warning",
40301         
40302         validClass: 'has-success',
40303         
40304         allowed: '0123456789',
40305         
40306         max_length: 15,
40307         
40308         /**
40309          * @cfg {String} defaultDialCode The default dial code when initializing the input
40310          */
40311         defaultDialCode: '+852',
40312         
40313         /**
40314          * @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
40315          */
40316         preferedCountries: false,
40317         
40318         getAutoCreate : function()
40319         {
40320             var data = Roo.bootstrap.PhoneInputData();
40321             var align = this.labelAlign || this.parentLabelAlign();
40322             var id = Roo.id();
40323             
40324             this.allCountries = [];
40325             this.dialCodeMapping = [];
40326             
40327             for (var i = 0; i < data.length; i++) {
40328               var c = data[i];
40329               this.allCountries[i] = {
40330                 name: c[0],
40331                 iso2: c[1],
40332                 dialCode: c[2],
40333                 priority: c[3] || 0,
40334                 areaCodes: c[4] || null
40335               };
40336               this.dialCodeMapping[c[2]] = {
40337                   name: c[0],
40338                   iso2: c[1],
40339                   priority: c[3] || 0,
40340                   areaCodes: c[4] || null
40341               };
40342             }
40343             
40344             var cfg = {
40345                 cls: 'form-group',
40346                 cn: []
40347             };
40348             
40349             var input =  {
40350                 tag: 'input',
40351                 id : id,
40352                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40353                 maxlength: this.max_length,
40354                 cls : 'form-control tel-input',
40355                 autocomplete: 'new-password'
40356             };
40357             
40358             var hiddenInput = {
40359                 tag: 'input',
40360                 type: 'hidden',
40361                 cls: 'hidden-tel-input'
40362             };
40363             
40364             if (this.name) {
40365                 hiddenInput.name = this.name;
40366             }
40367             
40368             if (this.disabled) {
40369                 input.disabled = true;
40370             }
40371             
40372             var flag_container = {
40373                 tag: 'div',
40374                 cls: 'flag-box',
40375                 cn: [
40376                     {
40377                         tag: 'div',
40378                         cls: 'flag'
40379                     },
40380                     {
40381                         tag: 'div',
40382                         cls: 'caret'
40383                     }
40384                 ]
40385             };
40386             
40387             var box = {
40388                 tag: 'div',
40389                 cls: this.hasFeedback ? 'has-feedback' : '',
40390                 cn: [
40391                     hiddenInput,
40392                     input,
40393                     {
40394                         tag: 'input',
40395                         cls: 'dial-code-holder',
40396                         disabled: true
40397                     }
40398                 ]
40399             };
40400             
40401             var container = {
40402                 cls: 'roo-select2-container input-group',
40403                 cn: [
40404                     flag_container,
40405                     box
40406                 ]
40407             };
40408             
40409             if (this.fieldLabel.length) {
40410                 var indicator = {
40411                     tag: 'i',
40412                     tooltip: 'This field is required'
40413                 };
40414                 
40415                 var label = {
40416                     tag: 'label',
40417                     'for':  id,
40418                     cls: 'control-label',
40419                     cn: []
40420                 };
40421                 
40422                 var label_text = {
40423                     tag: 'span',
40424                     html: this.fieldLabel
40425                 };
40426                 
40427                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40428                 label.cn = [
40429                     indicator,
40430                     label_text
40431                 ];
40432                 
40433                 if(this.indicatorpos == 'right') {
40434                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40435                     label.cn = [
40436                         label_text,
40437                         indicator
40438                     ];
40439                 }
40440                 
40441                 if(align == 'left') {
40442                     container = {
40443                         tag: 'div',
40444                         cn: [
40445                             container
40446                         ]
40447                     };
40448                     
40449                     if(this.labelWidth > 12){
40450                         label.style = "width: " + this.labelWidth + 'px';
40451                     }
40452                     if(this.labelWidth < 13 && this.labelmd == 0){
40453                         this.labelmd = this.labelWidth;
40454                     }
40455                     if(this.labellg > 0){
40456                         label.cls += ' col-lg-' + this.labellg;
40457                         input.cls += ' col-lg-' + (12 - this.labellg);
40458                     }
40459                     if(this.labelmd > 0){
40460                         label.cls += ' col-md-' + this.labelmd;
40461                         container.cls += ' col-md-' + (12 - this.labelmd);
40462                     }
40463                     if(this.labelsm > 0){
40464                         label.cls += ' col-sm-' + this.labelsm;
40465                         container.cls += ' col-sm-' + (12 - this.labelsm);
40466                     }
40467                     if(this.labelxs > 0){
40468                         label.cls += ' col-xs-' + this.labelxs;
40469                         container.cls += ' col-xs-' + (12 - this.labelxs);
40470                     }
40471                 }
40472             }
40473             
40474             cfg.cn = [
40475                 label,
40476                 container
40477             ];
40478             
40479             var settings = this;
40480             
40481             ['xs','sm','md','lg'].map(function(size){
40482                 if (settings[size]) {
40483                     cfg.cls += ' col-' + size + '-' + settings[size];
40484                 }
40485             });
40486             
40487             this.store = new Roo.data.Store({
40488                 proxy : new Roo.data.MemoryProxy({}),
40489                 reader : new Roo.data.JsonReader({
40490                     fields : [
40491                         {
40492                             'name' : 'name',
40493                             'type' : 'string'
40494                         },
40495                         {
40496                             'name' : 'iso2',
40497                             'type' : 'string'
40498                         },
40499                         {
40500                             'name' : 'dialCode',
40501                             'type' : 'string'
40502                         },
40503                         {
40504                             'name' : 'priority',
40505                             'type' : 'string'
40506                         },
40507                         {
40508                             'name' : 'areaCodes',
40509                             'type' : 'string'
40510                         }
40511                     ]
40512                 })
40513             });
40514             
40515             if(!this.preferedCountries) {
40516                 this.preferedCountries = [
40517                     'hk',
40518                     'gb',
40519                     'us'
40520                 ];
40521             }
40522             
40523             var p = this.preferedCountries.reverse();
40524             
40525             if(p) {
40526                 for (var i = 0; i < p.length; i++) {
40527                     for (var j = 0; j < this.allCountries.length; j++) {
40528                         if(this.allCountries[j].iso2 == p[i]) {
40529                             var t = this.allCountries[j];
40530                             this.allCountries.splice(j,1);
40531                             this.allCountries.unshift(t);
40532                         }
40533                     } 
40534                 }
40535             }
40536             
40537             this.store.proxy.data = {
40538                 success: true,
40539                 data: this.allCountries
40540             };
40541             
40542             return cfg;
40543         },
40544         
40545         initEvents : function()
40546         {
40547             this.createList();
40548             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40549             
40550             this.indicator = this.indicatorEl();
40551             this.flag = this.flagEl();
40552             this.dialCodeHolder = this.dialCodeHolderEl();
40553             
40554             this.trigger = this.el.select('div.flag-box',true).first();
40555             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40556             
40557             var _this = this;
40558             
40559             (function(){
40560                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40561                 _this.list.setWidth(lw);
40562             }).defer(100);
40563             
40564             this.list.on('mouseover', this.onViewOver, this);
40565             this.list.on('mousemove', this.onViewMove, this);
40566             this.inputEl().on("keyup", this.onKeyUp, this);
40567             this.inputEl().on("keypress", this.onKeyPress, this);
40568             
40569             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40570
40571             this.view = new Roo.View(this.list, this.tpl, {
40572                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40573             });
40574             
40575             this.view.on('click', this.onViewClick, this);
40576             this.setValue(this.defaultDialCode);
40577         },
40578         
40579         onTriggerClick : function(e)
40580         {
40581             Roo.log('trigger click');
40582             if(this.disabled){
40583                 return;
40584             }
40585             
40586             if(this.isExpanded()){
40587                 this.collapse();
40588                 this.hasFocus = false;
40589             }else {
40590                 this.store.load({});
40591                 this.hasFocus = true;
40592                 this.expand();
40593             }
40594         },
40595         
40596         isExpanded : function()
40597         {
40598             return this.list.isVisible();
40599         },
40600         
40601         collapse : function()
40602         {
40603             if(!this.isExpanded()){
40604                 return;
40605             }
40606             this.list.hide();
40607             Roo.get(document).un('mousedown', this.collapseIf, this);
40608             Roo.get(document).un('mousewheel', this.collapseIf, this);
40609             this.fireEvent('collapse', this);
40610             this.validate();
40611         },
40612         
40613         expand : function()
40614         {
40615             Roo.log('expand');
40616
40617             if(this.isExpanded() || !this.hasFocus){
40618                 return;
40619             }
40620             
40621             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40622             this.list.setWidth(lw);
40623             
40624             this.list.show();
40625             this.restrictHeight();
40626             
40627             Roo.get(document).on('mousedown', this.collapseIf, this);
40628             Roo.get(document).on('mousewheel', this.collapseIf, this);
40629             
40630             this.fireEvent('expand', this);
40631         },
40632         
40633         restrictHeight : function()
40634         {
40635             this.list.alignTo(this.inputEl(), this.listAlign);
40636             this.list.alignTo(this.inputEl(), this.listAlign);
40637         },
40638         
40639         onViewOver : function(e, t)
40640         {
40641             if(this.inKeyMode){
40642                 return;
40643             }
40644             var item = this.view.findItemFromChild(t);
40645             
40646             if(item){
40647                 var index = this.view.indexOf(item);
40648                 this.select(index, false);
40649             }
40650         },
40651
40652         // private
40653         onViewClick : function(view, doFocus, el, e)
40654         {
40655             var index = this.view.getSelectedIndexes()[0];
40656             
40657             var r = this.store.getAt(index);
40658             
40659             if(r){
40660                 this.onSelect(r, index);
40661             }
40662             if(doFocus !== false && !this.blockFocus){
40663                 this.inputEl().focus();
40664             }
40665         },
40666         
40667         onViewMove : function(e, t)
40668         {
40669             this.inKeyMode = false;
40670         },
40671         
40672         select : function(index, scrollIntoView)
40673         {
40674             this.selectedIndex = index;
40675             this.view.select(index);
40676             if(scrollIntoView !== false){
40677                 var el = this.view.getNode(index);
40678                 if(el){
40679                     this.list.scrollChildIntoView(el, false);
40680                 }
40681             }
40682         },
40683         
40684         createList : function()
40685         {
40686             this.list = Roo.get(document.body).createChild({
40687                 tag: 'ul',
40688                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40689                 style: 'display:none'
40690             });
40691             
40692             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40693         },
40694         
40695         collapseIf : function(e)
40696         {
40697             var in_combo  = e.within(this.el);
40698             var in_list =  e.within(this.list);
40699             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40700             
40701             if (in_combo || in_list || is_list) {
40702                 return;
40703             }
40704             this.collapse();
40705         },
40706         
40707         onSelect : function(record, index)
40708         {
40709             if(this.fireEvent('beforeselect', this, record, index) !== false){
40710                 
40711                 this.setFlagClass(record.data.iso2);
40712                 this.setDialCode(record.data.dialCode);
40713                 this.hasFocus = false;
40714                 this.collapse();
40715                 this.fireEvent('select', this, record, index);
40716             }
40717         },
40718         
40719         flagEl : function()
40720         {
40721             var flag = this.el.select('div.flag',true).first();
40722             if(!flag){
40723                 return false;
40724             }
40725             return flag;
40726         },
40727         
40728         dialCodeHolderEl : function()
40729         {
40730             var d = this.el.select('input.dial-code-holder',true).first();
40731             if(!d){
40732                 return false;
40733             }
40734             return d;
40735         },
40736         
40737         setDialCode : function(v)
40738         {
40739             this.dialCodeHolder.dom.value = '+'+v;
40740         },
40741         
40742         setFlagClass : function(n)
40743         {
40744             this.flag.dom.className = 'flag '+n;
40745         },
40746         
40747         getValue : function()
40748         {
40749             var v = this.inputEl().getValue();
40750             if(this.dialCodeHolder) {
40751                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40752             }
40753             return v;
40754         },
40755         
40756         setValue : function(v)
40757         {
40758             var d = this.getDialCode(v);
40759             
40760             //invalid dial code
40761             if(v.length == 0 || !d || d.length == 0) {
40762                 if(this.rendered){
40763                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40764                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40765                 }
40766                 return;
40767             }
40768             
40769             //valid dial code
40770             this.setFlagClass(this.dialCodeMapping[d].iso2);
40771             this.setDialCode(d);
40772             this.inputEl().dom.value = v.replace('+'+d,'');
40773             this.hiddenEl().dom.value = this.getValue();
40774             
40775             this.validate();
40776         },
40777         
40778         getDialCode : function(v)
40779         {
40780             v = v ||  '';
40781             
40782             if (v.length == 0) {
40783                 return this.dialCodeHolder.dom.value;
40784             }
40785             
40786             var dialCode = "";
40787             if (v.charAt(0) != "+") {
40788                 return false;
40789             }
40790             var numericChars = "";
40791             for (var i = 1; i < v.length; i++) {
40792               var c = v.charAt(i);
40793               if (!isNaN(c)) {
40794                 numericChars += c;
40795                 if (this.dialCodeMapping[numericChars]) {
40796                   dialCode = v.substr(1, i);
40797                 }
40798                 if (numericChars.length == 4) {
40799                   break;
40800                 }
40801               }
40802             }
40803             return dialCode;
40804         },
40805         
40806         reset : function()
40807         {
40808             this.setValue(this.defaultDialCode);
40809             this.validate();
40810         },
40811         
40812         hiddenEl : function()
40813         {
40814             return this.el.select('input.hidden-tel-input',true).first();
40815         },
40816         
40817         // after setting val
40818         onKeyUp : function(e){
40819             this.setValue(this.getValue());
40820         },
40821         
40822         onKeyPress : function(e){
40823             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40824                 e.stopEvent();
40825             }
40826         }
40827         
40828 });
40829 /**
40830  * @class Roo.bootstrap.MoneyField
40831  * @extends Roo.bootstrap.ComboBox
40832  * Bootstrap MoneyField class
40833  * 
40834  * @constructor
40835  * Create a new MoneyField.
40836  * @param {Object} config Configuration options
40837  */
40838
40839 Roo.bootstrap.MoneyField = function(config) {
40840     
40841     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40842     
40843 };
40844
40845 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40846     
40847     /**
40848      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40849      */
40850     allowDecimals : true,
40851     /**
40852      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40853      */
40854     decimalSeparator : ".",
40855     /**
40856      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40857      */
40858     decimalPrecision : 0,
40859     /**
40860      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40861      */
40862     allowNegative : true,
40863     /**
40864      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40865      */
40866     allowZero: true,
40867     /**
40868      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40869      */
40870     minValue : Number.NEGATIVE_INFINITY,
40871     /**
40872      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40873      */
40874     maxValue : Number.MAX_VALUE,
40875     /**
40876      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40877      */
40878     minText : "The minimum value for this field is {0}",
40879     /**
40880      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40881      */
40882     maxText : "The maximum value for this field is {0}",
40883     /**
40884      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40885      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40886      */
40887     nanText : "{0} is not a valid number",
40888     /**
40889      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40890      */
40891     castInt : true,
40892     /**
40893      * @cfg {String} defaults currency of the MoneyField
40894      * value should be in lkey
40895      */
40896     defaultCurrency : false,
40897     /**
40898      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40899      */
40900     thousandsDelimiter : false,
40901     /**
40902      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40903      */
40904     max_length: false,
40905     
40906     inputlg : 9,
40907     inputmd : 9,
40908     inputsm : 9,
40909     inputxs : 6,
40910     
40911     store : false,
40912     
40913     getAutoCreate : function()
40914     {
40915         var align = this.labelAlign || this.parentLabelAlign();
40916         
40917         var id = Roo.id();
40918
40919         var cfg = {
40920             cls: 'form-group',
40921             cn: []
40922         };
40923
40924         var input =  {
40925             tag: 'input',
40926             id : id,
40927             cls : 'form-control roo-money-amount-input',
40928             autocomplete: 'new-password'
40929         };
40930         
40931         var hiddenInput = {
40932             tag: 'input',
40933             type: 'hidden',
40934             id: Roo.id(),
40935             cls: 'hidden-number-input'
40936         };
40937         
40938         if(this.max_length) {
40939             input.maxlength = this.max_length; 
40940         }
40941         
40942         if (this.name) {
40943             hiddenInput.name = this.name;
40944         }
40945
40946         if (this.disabled) {
40947             input.disabled = true;
40948         }
40949
40950         var clg = 12 - this.inputlg;
40951         var cmd = 12 - this.inputmd;
40952         var csm = 12 - this.inputsm;
40953         var cxs = 12 - this.inputxs;
40954         
40955         var container = {
40956             tag : 'div',
40957             cls : 'row roo-money-field',
40958             cn : [
40959                 {
40960                     tag : 'div',
40961                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40962                     cn : [
40963                         {
40964                             tag : 'div',
40965                             cls: 'roo-select2-container input-group',
40966                             cn: [
40967                                 {
40968                                     tag : 'input',
40969                                     cls : 'form-control roo-money-currency-input',
40970                                     autocomplete: 'new-password',
40971                                     readOnly : 1,
40972                                     name : this.currencyName
40973                                 },
40974                                 {
40975                                     tag :'span',
40976                                     cls : 'input-group-addon',
40977                                     cn : [
40978                                         {
40979                                             tag: 'span',
40980                                             cls: 'caret'
40981                                         }
40982                                     ]
40983                                 }
40984                             ]
40985                         }
40986                     ]
40987                 },
40988                 {
40989                     tag : 'div',
40990                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40991                     cn : [
40992                         {
40993                             tag: 'div',
40994                             cls: this.hasFeedback ? 'has-feedback' : '',
40995                             cn: [
40996                                 input
40997                             ]
40998                         }
40999                     ]
41000                 }
41001             ]
41002             
41003         };
41004         
41005         if (this.fieldLabel.length) {
41006             var indicator = {
41007                 tag: 'i',
41008                 tooltip: 'This field is required'
41009             };
41010
41011             var label = {
41012                 tag: 'label',
41013                 'for':  id,
41014                 cls: 'control-label',
41015                 cn: []
41016             };
41017
41018             var label_text = {
41019                 tag: 'span',
41020                 html: this.fieldLabel
41021             };
41022
41023             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
41024             label.cn = [
41025                 indicator,
41026                 label_text
41027             ];
41028
41029             if(this.indicatorpos == 'right') {
41030                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
41031                 label.cn = [
41032                     label_text,
41033                     indicator
41034                 ];
41035             }
41036
41037             if(align == 'left') {
41038                 container = {
41039                     tag: 'div',
41040                     cn: [
41041                         container
41042                     ]
41043                 };
41044
41045                 if(this.labelWidth > 12){
41046                     label.style = "width: " + this.labelWidth + 'px';
41047                 }
41048                 if(this.labelWidth < 13 && this.labelmd == 0){
41049                     this.labelmd = this.labelWidth;
41050                 }
41051                 if(this.labellg > 0){
41052                     label.cls += ' col-lg-' + this.labellg;
41053                     input.cls += ' col-lg-' + (12 - this.labellg);
41054                 }
41055                 if(this.labelmd > 0){
41056                     label.cls += ' col-md-' + this.labelmd;
41057                     container.cls += ' col-md-' + (12 - this.labelmd);
41058                 }
41059                 if(this.labelsm > 0){
41060                     label.cls += ' col-sm-' + this.labelsm;
41061                     container.cls += ' col-sm-' + (12 - this.labelsm);
41062                 }
41063                 if(this.labelxs > 0){
41064                     label.cls += ' col-xs-' + this.labelxs;
41065                     container.cls += ' col-xs-' + (12 - this.labelxs);
41066                 }
41067             }
41068         }
41069
41070         cfg.cn = [
41071             label,
41072             container,
41073             hiddenInput
41074         ];
41075         
41076         var settings = this;
41077
41078         ['xs','sm','md','lg'].map(function(size){
41079             if (settings[size]) {
41080                 cfg.cls += ' col-' + size + '-' + settings[size];
41081             }
41082         });
41083         
41084         return cfg;
41085     },
41086     
41087     initEvents : function()
41088     {
41089         this.indicator = this.indicatorEl();
41090         
41091         this.initCurrencyEvent();
41092         
41093         this.initNumberEvent();
41094     },
41095     
41096     initCurrencyEvent : function()
41097     {
41098         if (!this.store) {
41099             throw "can not find store for combo";
41100         }
41101         
41102         this.store = Roo.factory(this.store, Roo.data);
41103         this.store.parent = this;
41104         
41105         this.createList();
41106         
41107         this.triggerEl = this.el.select('.input-group-addon', true).first();
41108         
41109         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41110         
41111         var _this = this;
41112         
41113         (function(){
41114             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41115             _this.list.setWidth(lw);
41116         }).defer(100);
41117         
41118         this.list.on('mouseover', this.onViewOver, this);
41119         this.list.on('mousemove', this.onViewMove, this);
41120         this.list.on('scroll', this.onViewScroll, this);
41121         
41122         if(!this.tpl){
41123             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41124         }
41125         
41126         this.view = new Roo.View(this.list, this.tpl, {
41127             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41128         });
41129         
41130         this.view.on('click', this.onViewClick, this);
41131         
41132         this.store.on('beforeload', this.onBeforeLoad, this);
41133         this.store.on('load', this.onLoad, this);
41134         this.store.on('loadexception', this.onLoadException, this);
41135         
41136         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41137             "up" : function(e){
41138                 this.inKeyMode = true;
41139                 this.selectPrev();
41140             },
41141
41142             "down" : function(e){
41143                 if(!this.isExpanded()){
41144                     this.onTriggerClick();
41145                 }else{
41146                     this.inKeyMode = true;
41147                     this.selectNext();
41148                 }
41149             },
41150
41151             "enter" : function(e){
41152                 this.collapse();
41153                 
41154                 if(this.fireEvent("specialkey", this, e)){
41155                     this.onViewClick(false);
41156                 }
41157                 
41158                 return true;
41159             },
41160
41161             "esc" : function(e){
41162                 this.collapse();
41163             },
41164
41165             "tab" : function(e){
41166                 this.collapse();
41167                 
41168                 if(this.fireEvent("specialkey", this, e)){
41169                     this.onViewClick(false);
41170                 }
41171                 
41172                 return true;
41173             },
41174
41175             scope : this,
41176
41177             doRelay : function(foo, bar, hname){
41178                 if(hname == 'down' || this.scope.isExpanded()){
41179                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41180                 }
41181                 return true;
41182             },
41183
41184             forceKeyDown: true
41185         });
41186         
41187         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41188         
41189     },
41190     
41191     initNumberEvent : function(e)
41192     {
41193         this.inputEl().on("keydown" , this.fireKey,  this);
41194         this.inputEl().on("focus", this.onFocus,  this);
41195         this.inputEl().on("blur", this.onBlur,  this);
41196         
41197         this.inputEl().relayEvent('keyup', this);
41198         
41199         if(this.indicator){
41200             this.indicator.addClass('invisible');
41201         }
41202  
41203         this.originalValue = this.getValue();
41204         
41205         if(this.validationEvent == 'keyup'){
41206             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41207             this.inputEl().on('keyup', this.filterValidation, this);
41208         }
41209         else if(this.validationEvent !== false){
41210             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41211         }
41212         
41213         if(this.selectOnFocus){
41214             this.on("focus", this.preFocus, this);
41215             
41216         }
41217         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41218             this.inputEl().on("keypress", this.filterKeys, this);
41219         } else {
41220             this.inputEl().relayEvent('keypress', this);
41221         }
41222         
41223         var allowed = "0123456789";
41224         
41225         if(this.allowDecimals){
41226             allowed += this.decimalSeparator;
41227         }
41228         
41229         if(this.allowNegative){
41230             allowed += "-";
41231         }
41232         
41233         if(this.thousandsDelimiter) {
41234             allowed += ",";
41235         }
41236         
41237         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41238         
41239         var keyPress = function(e){
41240             
41241             var k = e.getKey();
41242             
41243             var c = e.getCharCode();
41244             
41245             if(
41246                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41247                     allowed.indexOf(String.fromCharCode(c)) === -1
41248             ){
41249                 e.stopEvent();
41250                 return;
41251             }
41252             
41253             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41254                 return;
41255             }
41256             
41257             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41258                 e.stopEvent();
41259             }
41260         };
41261         
41262         this.inputEl().on("keypress", keyPress, this);
41263         
41264     },
41265     
41266     onTriggerClick : function(e)
41267     {   
41268         if(this.disabled){
41269             return;
41270         }
41271         
41272         this.page = 0;
41273         this.loadNext = false;
41274         
41275         if(this.isExpanded()){
41276             this.collapse();
41277             return;
41278         }
41279         
41280         this.hasFocus = true;
41281         
41282         if(this.triggerAction == 'all') {
41283             this.doQuery(this.allQuery, true);
41284             return;
41285         }
41286         
41287         this.doQuery(this.getRawValue());
41288     },
41289     
41290     getCurrency : function()
41291     {   
41292         var v = this.currencyEl().getValue();
41293         
41294         return v;
41295     },
41296     
41297     restrictHeight : function()
41298     {
41299         this.list.alignTo(this.currencyEl(), this.listAlign);
41300         this.list.alignTo(this.currencyEl(), this.listAlign);
41301     },
41302     
41303     onViewClick : function(view, doFocus, el, e)
41304     {
41305         var index = this.view.getSelectedIndexes()[0];
41306         
41307         var r = this.store.getAt(index);
41308         
41309         if(r){
41310             this.onSelect(r, index);
41311         }
41312     },
41313     
41314     onSelect : function(record, index){
41315         
41316         if(this.fireEvent('beforeselect', this, record, index) !== false){
41317         
41318             this.setFromCurrencyData(index > -1 ? record.data : false);
41319             
41320             this.collapse();
41321             
41322             this.fireEvent('select', this, record, index);
41323         }
41324     },
41325     
41326     setFromCurrencyData : function(o)
41327     {
41328         var currency = '';
41329         
41330         this.lastCurrency = o;
41331         
41332         if (this.currencyField) {
41333             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41334         } else {
41335             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41336         }
41337         
41338         this.lastSelectionText = currency;
41339         
41340         //setting default currency
41341         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41342             this.setCurrency(this.defaultCurrency);
41343             return;
41344         }
41345         
41346         this.setCurrency(currency);
41347     },
41348     
41349     setFromData : function(o)
41350     {
41351         var c = {};
41352         
41353         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41354         
41355         this.setFromCurrencyData(c);
41356         
41357         var value = '';
41358         
41359         if (this.name) {
41360             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41361         } else {
41362             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41363         }
41364         
41365         this.setValue(value);
41366         
41367     },
41368     
41369     setCurrency : function(v)
41370     {   
41371         this.currencyValue = v;
41372         
41373         if(this.rendered){
41374             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41375             this.validate();
41376         }
41377     },
41378     
41379     setValue : function(v)
41380     {
41381         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41382         
41383         this.value = v;
41384         
41385         if(this.rendered){
41386             
41387             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41388             
41389             this.inputEl().dom.value = (v == '') ? '' :
41390                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41391             
41392             if(!this.allowZero && v === '0') {
41393                 this.hiddenEl().dom.value = '';
41394                 this.inputEl().dom.value = '';
41395             }
41396             
41397             this.validate();
41398         }
41399     },
41400     
41401     getRawValue : function()
41402     {
41403         var v = this.inputEl().getValue();
41404         
41405         return v;
41406     },
41407     
41408     getValue : function()
41409     {
41410         return this.fixPrecision(this.parseValue(this.getRawValue()));
41411     },
41412     
41413     parseValue : function(value)
41414     {
41415         if(this.thousandsDelimiter) {
41416             value += "";
41417             r = new RegExp(",", "g");
41418             value = value.replace(r, "");
41419         }
41420         
41421         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41422         return isNaN(value) ? '' : value;
41423         
41424     },
41425     
41426     fixPrecision : function(value)
41427     {
41428         if(this.thousandsDelimiter) {
41429             value += "";
41430             r = new RegExp(",", "g");
41431             value = value.replace(r, "");
41432         }
41433         
41434         var nan = isNaN(value);
41435         
41436         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41437             return nan ? '' : value;
41438         }
41439         return parseFloat(value).toFixed(this.decimalPrecision);
41440     },
41441     
41442     decimalPrecisionFcn : function(v)
41443     {
41444         return Math.floor(v);
41445     },
41446     
41447     validateValue : function(value)
41448     {
41449         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41450             return false;
41451         }
41452         
41453         var num = this.parseValue(value);
41454         
41455         if(isNaN(num)){
41456             this.markInvalid(String.format(this.nanText, value));
41457             return false;
41458         }
41459         
41460         if(num < this.minValue){
41461             this.markInvalid(String.format(this.minText, this.minValue));
41462             return false;
41463         }
41464         
41465         if(num > this.maxValue){
41466             this.markInvalid(String.format(this.maxText, this.maxValue));
41467             return false;
41468         }
41469         
41470         return true;
41471     },
41472     
41473     validate : function()
41474     {
41475         if(this.disabled || this.allowBlank){
41476             this.markValid();
41477             return true;
41478         }
41479         
41480         var currency = this.getCurrency();
41481         
41482         if(this.validateValue(this.getRawValue()) && currency.length){
41483             this.markValid();
41484             return true;
41485         }
41486         
41487         this.markInvalid();
41488         return false;
41489     },
41490     
41491     getName: function()
41492     {
41493         return this.name;
41494     },
41495     
41496     beforeBlur : function()
41497     {
41498         if(!this.castInt){
41499             return;
41500         }
41501         
41502         var v = this.parseValue(this.getRawValue());
41503         
41504         if(v || v == 0){
41505             this.setValue(v);
41506         }
41507     },
41508     
41509     onBlur : function()
41510     {
41511         this.beforeBlur();
41512         
41513         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41514             //this.el.removeClass(this.focusClass);
41515         }
41516         
41517         this.hasFocus = false;
41518         
41519         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41520             this.validate();
41521         }
41522         
41523         var v = this.getValue();
41524         
41525         if(String(v) !== String(this.startValue)){
41526             this.fireEvent('change', this, v, this.startValue);
41527         }
41528         
41529         this.fireEvent("blur", this);
41530     },
41531     
41532     inputEl : function()
41533     {
41534         return this.el.select('.roo-money-amount-input', true).first();
41535     },
41536     
41537     currencyEl : function()
41538     {
41539         return this.el.select('.roo-money-currency-input', true).first();
41540     },
41541     
41542     hiddenEl : function()
41543     {
41544         return this.el.select('input.hidden-number-input',true).first();
41545     }
41546     
41547 });/**
41548 *    This script refer to:
41549 *    Title: Signature Pad
41550 *    Author: szimek
41551 *    Availability: https://github.com/szimek/signature_pad
41552 **/
41553
41554 /**
41555  * @class Roo.bootstrap.BezierSignature
41556  * @extends Roo.bootstrap.Component
41557  * Bootstrap BezierSignature class
41558  * 
41559  * @constructor
41560  * Create a new BezierSignature
41561  * @param {Object} config The config object
41562  */
41563
41564 Roo.bootstrap.BezierSignature = function(config){
41565     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41566     this.addEvents({
41567         "resize" : true
41568     });
41569 };
41570
41571 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,  {
41572     
41573     curve_data: [],
41574     
41575     is_empty: true,
41576     
41577     mouse_btn_down: true,
41578     
41579     /**
41580      * @cfg(int) canvas height
41581      */
41582     canvas_height: '200px',
41583     
41584     /**
41585      * @cfg(float or function) Radius of a single dot.
41586      */ 
41587     dot_size: false,
41588     
41589     /**
41590      * @cfg(float) Minimum width of a line. Defaults to 0.5.
41591      */
41592     min_width: 0.5,
41593     
41594     /**
41595      * @cfg(float) Maximum width of a line. Defaults to 2.5.
41596      */
41597     max_width: 2.5,
41598     
41599     /**
41600      * @cfg(integer) Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41601      */
41602     throttle: 16,
41603     
41604     /**
41605      * @cfg(integer) Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41606      */
41607     min_distance: 5,
41608     
41609     /**
41610      * @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.
41611      */
41612     bg_color: 'rgba(0, 0, 0, 0)',
41613     
41614     /**
41615      * @cfg(string) Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41616      */
41617     dot_color: 'black',
41618     
41619     /**
41620      * @cfg(float) Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41621      */
41622     velocity_filter_weight: 0.7,
41623     
41624     /**
41625      * @cfg(function) Callback when stroke begin.
41626      */
41627     onBegin: false,
41628     
41629     /**
41630      * @cfg(function) Callback when stroke end.
41631      */
41632     onEnd: false,
41633     
41634     getAutoCreate : function()
41635     {
41636         var cls = 'roo-signature column';
41637         
41638         if(this.cls){
41639             cls += ' ' + this.cls;
41640         }
41641         
41642         var col_sizes = [
41643             'lg',
41644             'md',
41645             'sm',
41646             'xs'
41647         ];
41648         
41649         for(var i = 0; i < col_sizes.length; i++) {
41650             if(this[col_sizes[i]]) {
41651                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
41652             }
41653         }
41654         
41655         var cfg = {
41656             tag: 'div',
41657             cls: cls,
41658             cn: [
41659                 {
41660                     tag: 'div',
41661                     cls: 'roo-signature-body',
41662                     cn: [
41663                         {
41664                             tag: 'canvas',
41665                             cls: 'roo-signature-body-canvas',
41666                             height: this.canvas_height,
41667                             width: this.canvas_width
41668                         }
41669                     ]
41670                 },
41671                 {
41672                     tag: 'input',
41673                     type: 'file',
41674                     style: 'display: none'
41675                 }
41676             ]
41677         };
41678         
41679         return cfg;
41680     },
41681     
41682     initEvents: function() 
41683     {
41684         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
41685         
41686         var canvas = this.canvasEl();
41687         
41688         // mouse && touch event swapping...
41689         canvas.dom.style.touchAction = 'none';
41690         canvas.dom.style.msTouchAction = 'none';
41691         
41692         this.mouse_btn_down = false;
41693         canvas.on('mousedown', this._handleMouseDown, this);
41694         canvas.on('mousemove', this._handleMouseMove, this);
41695         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
41696         
41697         if (window.PointerEvent) {
41698             canvas.on('pointerdown', this._handleMouseDown, this);
41699             canvas.on('pointermove', this._handleMouseMove, this);
41700             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
41701         }
41702         
41703         if ('ontouchstart' in window) {
41704             canvas.on('touchstart', this._handleTouchStart, this);
41705             canvas.on('touchmove', this._handleTouchMove, this);
41706             canvas.on('touchend', this._handleTouchEnd, this);
41707         }
41708         
41709         Roo.EventManager.onWindowResize(this.resize, this, true);
41710         
41711         // file input event
41712         this.fileEl().on('change', this.uploadImage, this);
41713         
41714         this.clear();
41715         
41716         this.resize();
41717     },
41718     
41719     resize: function(){
41720         
41721         var canvas = this.canvasEl().dom;
41722         var ctx = this.canvasElCtx();
41723         var img_data = false;
41724         
41725         if(canvas.width > 0) {
41726             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
41727         }
41728         // setting canvas width will clean img data
41729         canvas.width = 0;
41730         
41731         var style = window.getComputedStyle ? 
41732             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
41733             
41734         var padding_left = parseInt(style.paddingLeft) || 0;
41735         var padding_right = parseInt(style.paddingRight) || 0;
41736         
41737         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
41738         
41739         if(img_data) {
41740             ctx.putImageData(img_data, 0, 0);
41741         }
41742     },
41743     
41744     _handleMouseDown: function(e)
41745     {
41746         if (e.browserEvent.which === 1) {
41747             this.mouse_btn_down = true;
41748             this.strokeBegin(e);
41749         }
41750     },
41751     
41752     _handleMouseMove: function (e)
41753     {
41754         if (this.mouse_btn_down) {
41755             this.strokeMoveUpdate(e);
41756         }
41757     },
41758     
41759     _handleMouseUp: function (e)
41760     {
41761         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
41762             this.mouse_btn_down = false;
41763             this.strokeEnd(e);
41764         }
41765     },
41766     
41767     _handleTouchStart: function (e) {
41768         
41769         e.preventDefault();
41770         if (e.browserEvent.targetTouches.length === 1) {
41771             // var touch = e.browserEvent.changedTouches[0];
41772             // this.strokeBegin(touch);
41773             
41774              this.strokeBegin(e); // assume e catching the correct xy...
41775         }
41776     },
41777     
41778     _handleTouchMove: function (e) {
41779         e.preventDefault();
41780         // var touch = event.targetTouches[0];
41781         // _this._strokeMoveUpdate(touch);
41782         this.strokeMoveUpdate(e);
41783     },
41784     
41785     _handleTouchEnd: function (e) {
41786         var wasCanvasTouched = e.target === this.canvasEl().dom;
41787         if (wasCanvasTouched) {
41788             e.preventDefault();
41789             // var touch = event.changedTouches[0];
41790             // _this._strokeEnd(touch);
41791             this.strokeEnd(e);
41792         }
41793     },
41794     
41795     reset: function () {
41796         this._lastPoints = [];
41797         this._lastVelocity = 0;
41798         this._lastWidth = (this.min_width + this.max_width) / 2;
41799         this.canvasElCtx().fillStyle = this.dot_color;
41800     },
41801     
41802     strokeMoveUpdate: function(e)
41803     {
41804         this.strokeUpdate(e);
41805         
41806         if (this.throttle) {
41807             this.throttle(this.strokeUpdate, this.throttle);
41808         }
41809         else {
41810             this.strokeUpdate(e);
41811         }
41812     },
41813     
41814     strokeBegin: function(e)
41815     {
41816         var newPointGroup = {
41817             color: this.dot_color,
41818             points: []
41819         };
41820         
41821         if (typeof this.onBegin === 'function') {
41822             this.onBegin(e);
41823         }
41824         
41825         this.curve_data.push(newPointGroup);
41826         this.reset();
41827         this.strokeUpdate(e);
41828     },
41829     
41830     strokeUpdate: function(e)
41831     {
41832         var rect = this.canvasEl().dom.getBoundingClientRect();
41833         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
41834         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
41835         var lastPoints = lastPointGroup.points;
41836         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
41837         var isLastPointTooClose = lastPoint
41838             ? point.distanceTo(lastPoint) <= this.min_distance
41839             : false;
41840         var color = lastPointGroup.color;
41841         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
41842             var curve = this.addPoint(point);
41843             if (!lastPoint) {
41844                 this.drawDot({color: color, point: point});
41845             }
41846             else if (curve) {
41847                 this.drawCurve({color: color, curve: curve});
41848             }
41849             lastPoints.push({
41850                 time: point.time,
41851                 x: point.x,
41852                 y: point.y
41853             });
41854         }
41855     },
41856     
41857     strokeEnd: function(e)
41858     {
41859         this.strokeUpdate(e);
41860         if (typeof this.onEnd === 'function') {
41861             this.onEnd(e);
41862         }
41863     },
41864     
41865     addPoint:  function (point) {
41866         var _lastPoints = this._lastPoints;
41867         _lastPoints.push(point);
41868         if (_lastPoints.length > 2) {
41869             if (_lastPoints.length === 3) {
41870                 _lastPoints.unshift(_lastPoints[0]);
41871             }
41872             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
41873             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
41874             _lastPoints.shift();
41875             return curve;
41876         }
41877         return null;
41878     },
41879     
41880     calculateCurveWidths: function (startPoint, endPoint) {
41881         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
41882             (1 - this.velocity_filter_weight) * this._lastVelocity;
41883
41884         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
41885         var widths = {
41886             end: newWidth,
41887             start: this._lastWidth
41888         };
41889         
41890         this._lastVelocity = velocity;
41891         this._lastWidth = newWidth;
41892         return widths;
41893     },
41894     
41895     drawDot: function (_a) {
41896         var color = _a.color, point = _a.point;
41897         var ctx = this.canvasElCtx();
41898         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
41899         ctx.beginPath();
41900         this.drawCurveSegment(point.x, point.y, width);
41901         ctx.closePath();
41902         ctx.fillStyle = color;
41903         ctx.fill();
41904     },
41905     
41906     drawCurve: function (_a) {
41907         var color = _a.color, curve = _a.curve;
41908         var ctx = this.canvasElCtx();
41909         var widthDelta = curve.endWidth - curve.startWidth;
41910         var drawSteps = Math.floor(curve.length()) * 2;
41911         ctx.beginPath();
41912         ctx.fillStyle = color;
41913         for (var i = 0; i < drawSteps; i += 1) {
41914         var t = i / drawSteps;
41915         var tt = t * t;
41916         var ttt = tt * t;
41917         var u = 1 - t;
41918         var uu = u * u;
41919         var uuu = uu * u;
41920         var x = uuu * curve.startPoint.x;
41921         x += 3 * uu * t * curve.control1.x;
41922         x += 3 * u * tt * curve.control2.x;
41923         x += ttt * curve.endPoint.x;
41924         var y = uuu * curve.startPoint.y;
41925         y += 3 * uu * t * curve.control1.y;
41926         y += 3 * u * tt * curve.control2.y;
41927         y += ttt * curve.endPoint.y;
41928         var width = curve.startWidth + ttt * widthDelta;
41929         this.drawCurveSegment(x, y, width);
41930         }
41931         ctx.closePath();
41932         ctx.fill();
41933     },
41934     
41935     drawCurveSegment: function (x, y, width) {
41936         var ctx = this.canvasElCtx();
41937         ctx.moveTo(x, y);
41938         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
41939         this.is_empty = false;
41940     },
41941     
41942     clear: function()
41943     {
41944         var ctx = this.canvasElCtx();
41945         var canvas = this.canvasEl().dom;
41946         ctx.fillStyle = this.bg_color;
41947         ctx.clearRect(0, 0, canvas.width, canvas.height);
41948         ctx.fillRect(0, 0, canvas.width, canvas.height);
41949         this.curve_data = [];
41950         this.reset();
41951         this.is_empty = true;
41952     },
41953     
41954     fileEl: function()
41955     {
41956         return  this.el.select('input',true).first();
41957     },
41958     
41959     canvasEl: function()
41960     {
41961         return this.el.select('canvas',true).first();
41962     },
41963     
41964     canvasElCtx: function()
41965     {
41966         return this.el.select('canvas',true).first().dom.getContext('2d');
41967     },
41968     
41969     getImage: function(type)
41970     {
41971         if(this.is_empty) {
41972             return false;
41973         }
41974         
41975         // encryption ?
41976         return this.canvasEl().dom.toDataURL('image/'+type, 1);
41977     },
41978     
41979     drawFromImage: function(img_src)
41980     {
41981         var img = new Image();
41982         
41983         img.onload = function(){
41984             this.canvasElCtx().drawImage(img, 0, 0);
41985         }.bind(this);
41986         
41987         img.src = img_src;
41988         
41989         this.is_empty = false;
41990     },
41991     
41992     selectImage: function()
41993     {
41994         this.fileEl().dom.click();
41995     },
41996     
41997     uploadImage: function(e)
41998     {
41999         var reader = new FileReader();
42000         
42001         reader.onload = function(e){
42002             var img = new Image();
42003             img.onload = function(){
42004                 this.reset();
42005                 this.canvasElCtx().drawImage(img, 0, 0);
42006             }.bind(this);
42007             img.src = e.target.result;
42008         }.bind(this);
42009         
42010         reader.readAsDataURL(e.target.files[0]);
42011     },
42012     
42013     // Bezier Point Constructor
42014     Point: (function () {
42015         function Point(x, y, time) {
42016             this.x = x;
42017             this.y = y;
42018             this.time = time || Date.now();
42019         }
42020         Point.prototype.distanceTo = function (start) {
42021             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
42022         };
42023         Point.prototype.equals = function (other) {
42024             return this.x === other.x && this.y === other.y && this.time === other.time;
42025         };
42026         Point.prototype.velocityFrom = function (start) {
42027             return this.time !== start.time
42028             ? this.distanceTo(start) / (this.time - start.time)
42029             : 0;
42030         };
42031         return Point;
42032     }()),
42033     
42034     
42035     // Bezier Constructor
42036     Bezier: (function () {
42037         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
42038             this.startPoint = startPoint;
42039             this.control2 = control2;
42040             this.control1 = control1;
42041             this.endPoint = endPoint;
42042             this.startWidth = startWidth;
42043             this.endWidth = endWidth;
42044         }
42045         Bezier.fromPoints = function (points, widths, scope) {
42046             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42047             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42048             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42049         };
42050         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42051             var dx1 = s1.x - s2.x;
42052             var dy1 = s1.y - s2.y;
42053             var dx2 = s2.x - s3.x;
42054             var dy2 = s2.y - s3.y;
42055             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42056             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42057             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42058             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42059             var dxm = m1.x - m2.x;
42060             var dym = m1.y - m2.y;
42061             var k = l2 / (l1 + l2);
42062             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42063             var tx = s2.x - cm.x;
42064             var ty = s2.y - cm.y;
42065             return {
42066                 c1: new scope.Point(m1.x + tx, m1.y + ty),
42067                 c2: new scope.Point(m2.x + tx, m2.y + ty)
42068             };
42069         };
42070         Bezier.prototype.length = function () {
42071             var steps = 10;
42072             var length = 0;
42073             var px;
42074             var py;
42075             for (var i = 0; i <= steps; i += 1) {
42076                 var t = i / steps;
42077                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42078                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42079                 if (i > 0) {
42080                     var xdiff = cx - px;
42081                     var ydiff = cy - py;
42082                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42083                 }
42084                 px = cx;
42085                 py = cy;
42086             }
42087             return length;
42088         };
42089         Bezier.prototype.point = function (t, start, c1, c2, end) {
42090             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42091             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42092             + (3.0 * c2 * (1.0 - t) * t * t)
42093             + (end * t * t * t);
42094         };
42095         return Bezier;
42096     }()),
42097     
42098     throttle: function(fn, wait) {
42099       if (wait === void 0) { wait = 250; }
42100       var previous = 0;
42101       var timeout = null;
42102       var result;
42103       var storedContext;
42104       var storedArgs;
42105       var later = function () {
42106           previous = Date.now();
42107           timeout = null;
42108           result = fn.apply(storedContext, storedArgs);
42109           if (!timeout) {
42110               storedContext = null;
42111               storedArgs = [];
42112           }
42113       };
42114       return function wrapper() {
42115           var args = [];
42116           for (var _i = 0; _i < arguments.length; _i++) {
42117               args[_i] = arguments[_i];
42118           }
42119           var now = Date.now();
42120           var remaining = wait - (now - previous);
42121           storedContext = this;
42122           storedArgs = args;
42123           if (remaining <= 0 || remaining > wait) {
42124               if (timeout) {
42125                   clearTimeout(timeout);
42126                   timeout = null;
42127               }
42128               previous = now;
42129               result = fn.apply(storedContext, storedArgs);
42130               if (!timeout) {
42131                   storedContext = null;
42132                   storedArgs = [];
42133               }
42134           }
42135           else if (!timeout) {
42136               timeout = window.setTimeout(later, remaining);
42137           }
42138           return result;
42139       };
42140   }
42141   
42142 });
42143
42144  
42145
42146