roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = (
7         function() {
8                 var ret=3;
9                 Roo.each(document.styleSheets[0], function(s) {
10                     if (s.href.match(/css-bootstrap4/)) {
11                         ret=4;
12                     }
13                 });
14         return ret;
15 })();/*
16  * - LGPL
17  *
18  * base class for bootstrap elements.
19  * 
20  */
21
22 Roo.bootstrap = Roo.bootstrap || {};
23 /**
24  * @class Roo.bootstrap.Component
25  * @extends Roo.Component
26  * Bootstrap Component base class
27  * @cfg {String} cls css class
28  * @cfg {String} style any extra css
29  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
30  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
31  * @cfg {string} dataId cutomer id
32  * @cfg {string} name Specifies name attribute
33  * @cfg {string} tooltip  Text for the tooltip
34  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
35  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
36  
37  * @constructor
38  * Do not use directly - it does not do anything..
39  * @param {Object} config The config object
40  */
41
42
43
44 Roo.bootstrap.Component = function(config){
45     Roo.bootstrap.Component.superclass.constructor.call(this, config);
46        
47     this.addEvents({
48         /**
49          * @event childrenrendered
50          * Fires when the children have been rendered..
51          * @param {Roo.bootstrap.Component} this
52          */
53         "childrenrendered" : true
54         
55         
56         
57     });
58     
59     
60 };
61
62 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
63     
64     
65     allowDomMove : false, // to stop relocations in parent onRender...
66     
67     cls : false,
68     
69     style : false,
70     
71     autoCreate : false,
72     
73     tooltip : null,
74     /**
75      * Initialize Events for the element
76      */
77     initEvents : function() { },
78     
79     xattr : false,
80     
81     parentId : false,
82     
83     can_build_overlaid : true,
84     
85     container_method : false,
86     
87     dataId : false,
88     
89     name : false,
90     
91     parent: function() {
92         // returns the parent component..
93         return Roo.ComponentMgr.get(this.parentId)
94         
95         
96     },
97     
98     // private
99     onRender : function(ct, position)
100     {
101        // Roo.log("Call onRender: " + this.xtype);
102         
103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
104         
105         if(this.el){
106             if (this.el.attr('xtype')) {
107                 this.el.attr('xtypex', this.el.attr('xtype'));
108                 this.el.dom.removeAttribute('xtype');
109                 
110                 this.initEvents();
111             }
112             
113             return;
114         }
115         
116          
117         
118         var cfg = Roo.apply({},  this.getAutoCreate());
119         
120         cfg.id = this.id || Roo.id();
121         
122         // fill in the extra attributes 
123         if (this.xattr && typeof(this.xattr) =='object') {
124             for (var i in this.xattr) {
125                 cfg[i] = this.xattr[i];
126             }
127         }
128         
129         if(this.dataId){
130             cfg.dataId = this.dataId;
131         }
132         
133         if (this.cls) {
134             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
135         }
136         
137         if (this.style) { // fixme needs to support more complex style data.
138             cfg.style = this.style;
139         }
140         
141         if(this.name){
142             cfg.name = this.name;
143         }
144         
145         this.el = ct.createChild(cfg, position);
146         
147         if (this.tooltip) {
148             this.tooltipEl().attr('tooltip', this.tooltip);
149         }
150         
151         if(this.tabIndex !== undefined){
152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
153         }
154         
155         this.initEvents();
156         
157     },
158     /**
159      * Fetch the element to add children to
160      * @return {Roo.Element} defaults to this.el
161      */
162     getChildContainer : function()
163     {
164         return this.el;
165     },
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass(['hidden','d-none']);
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass(['hidden','d-none']);
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438  /*
439  * - LGPL
440  *
441  * Body
442  *
443  */
444
445 /**
446  * @class Roo.bootstrap.Body
447  * @extends Roo.bootstrap.Component
448  * Bootstrap Body class
449  *
450  * @constructor
451  * Create a new body
452  * @param {Object} config The config object
453  */
454
455 Roo.bootstrap.Body = function(config){
456
457     config = config || {};
458
459     Roo.bootstrap.Body.superclass.constructor.call(this, config);
460     this.el = Roo.get(config.el ? config.el : document.body );
461     if (this.cls && this.cls.length) {
462         Roo.get(document.body).addClass(this.cls);
463     }
464 };
465
466 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
467
468     is_body : true,// just to make sure it's constructed?
469
470         autoCreate : {
471         cls: 'container'
472     },
473     onRender : function(ct, position)
474     {
475        /* Roo.log("Roo.bootstrap.Body - onRender");
476         if (this.cls && this.cls.length) {
477             Roo.get(document.body).addClass(this.cls);
478         }
479         // style??? xttr???
480         */
481     }
482
483
484
485
486 });
487 /*
488  * - LGPL
489  *
490  * button group
491  * 
492  */
493
494
495 /**
496  * @class Roo.bootstrap.ButtonGroup
497  * @extends Roo.bootstrap.Component
498  * Bootstrap ButtonGroup class
499  * @cfg {String} size lg | sm | xs (default empty normal)
500  * @cfg {String} align vertical | justified  (default none)
501  * @cfg {String} direction up | down (default down)
502  * @cfg {Boolean} toolbar false | true
503  * @cfg {Boolean} btn true | false
504  * 
505  * 
506  * @constructor
507  * Create a new Input
508  * @param {Object} config The config object
509  */
510
511 Roo.bootstrap.ButtonGroup = function(config){
512     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
513 };
514
515 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
516     
517     size: '',
518     align: '',
519     direction: '',
520     toolbar: false,
521     btn: true,
522
523     getAutoCreate : function(){
524         var cfg = {
525             cls: 'btn-group',
526             html : null
527         };
528         
529         cfg.html = this.html || cfg.html;
530         
531         if (this.toolbar) {
532             cfg = {
533                 cls: 'btn-toolbar',
534                 html: null
535             };
536             
537             return cfg;
538         }
539         
540         if (['vertical','justified'].indexOf(this.align)!==-1) {
541             cfg.cls = 'btn-group-' + this.align;
542             
543             if (this.align == 'justified') {
544                 console.log(this.items);
545             }
546         }
547         
548         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
549             cfg.cls += ' btn-group-' + this.size;
550         }
551         
552         if (this.direction == 'up') {
553             cfg.cls += ' dropup' ;
554         }
555         
556         return cfg;
557     },
558     /**
559      * Add a button to the group (similar to NavItem API.)
560      */
561     addItem : function(cfg)
562     {
563         var cn = new Roo.bootstrap.Button(cfg);
564         //this.register(cn);
565         cn.parentId = this.id;
566         cn.onRender(this.el, null);
567         return cn;
568     }
569    
570 });
571
572  /*
573  * - LGPL
574  *
575  * button
576  * 
577  */
578
579 /**
580  * @class Roo.bootstrap.Button
581  * @extends Roo.bootstrap.Component
582  * Bootstrap Button class
583  * @cfg {String} html The button content
584  * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
585  * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
586  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
587  * @cfg {String} size ( lg | sm | xs)
588  * @cfg {String} tag ( a | input | submit)
589  * @cfg {String} href empty or href
590  * @cfg {Boolean} disabled default false;
591  * @cfg {Boolean} isClose default false;
592  * @cfg {String} glyphicon depricated - use fa
593  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
594  * @cfg {String} badge text for badge
595  * @cfg {String} theme (default|glow)  
596  * @cfg {Boolean} inverse dark themed version
597  * @cfg {Boolean} toggle is it a slidy toggle button
598  * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
599  * @cfg {String} ontext text for on slidy toggle state
600  * @cfg {String} offtext text for off slidy toggle state
601  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
602  * @cfg {Boolean} removeClass remove the standard class..
603  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
604  * 
605  * @constructor
606  * Create a new button
607  * @param {Object} config The config object
608  */
609
610
611 Roo.bootstrap.Button = function(config){
612     Roo.bootstrap.Button.superclass.constructor.call(this, config);
613     this.weightClass = ["btn-default btn-outline-secondary", 
614                        "btn-primary", 
615                        "btn-success", 
616                        "btn-info", 
617                        "btn-warning",
618                        "btn-danger",
619                        "btn-link"
620                       ],  
621     this.addEvents({
622         // raw events
623         /**
624          * @event click
625          * When a butotn is pressed
626          * @param {Roo.bootstrap.Button} btn
627          * @param {Roo.EventObject} e
628          */
629         "click" : true,
630          /**
631          * @event toggle
632          * After the button has been toggles
633          * @param {Roo.bootstrap.Button} btn
634          * @param {Roo.EventObject} e
635          * @param {boolean} pressed (also available as button.pressed)
636          */
637         "toggle" : true
638     });
639 };
640
641 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
642     html: false,
643     active: false,
644     weight: '',
645     badge_weight: '',
646     outline : false,
647     size: '',
648     tag: 'button',
649     href: '',
650     disabled: false,
651     isClose: false,
652     glyphicon: '',
653     fa: '',
654     badge: '',
655     theme: 'default',
656     inverse: false,
657     
658     toggle: false,
659     ontext: 'ON',
660     offtext: 'OFF',
661     defaulton: true,
662     preventDefault: true,
663     removeClass: false,
664     name: false,
665     target: false,
666      
667     pressed : null,
668      
669     
670     getAutoCreate : function(){
671         
672         var cfg = {
673             tag : 'button',
674             cls : 'roo-button',
675             html: ''
676         };
677         
678         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
679             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
680             this.tag = 'button';
681         } else {
682             cfg.tag = this.tag;
683         }
684         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
685         
686         if (this.toggle == true) {
687             cfg={
688                 tag: 'div',
689                 cls: 'slider-frame roo-button',
690                 cn: [
691                     {
692                         tag: 'span',
693                         'data-on-text':'ON',
694                         'data-off-text':'OFF',
695                         cls: 'slider-button',
696                         html: this.offtext
697                     }
698                 ]
699             };
700             
701             if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
702                 cfg.cls += ' '+this.weight;
703             }
704             
705             return cfg;
706         }
707         
708         if (this.isClose) {
709             cfg.cls += ' close';
710             
711             cfg["aria-hidden"] = true;
712             
713             cfg.html = "&times;";
714             
715             return cfg;
716         }
717         
718          
719         if (this.theme==='default') {
720             cfg.cls = 'btn roo-button';
721             
722             //if (this.parentType != 'Navbar') {
723             this.weight = this.weight.length ?  this.weight : 'default';
724             //}
725             if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
726                 
727                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
728                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
729                 cfg.cls += ' btn-' + outline + weight;
730                 if (this.weight == 'default') {
731                     // BC
732                     cfg.cls += ' btn-' + this.weight;
733                 }
734             }
735         } else if (this.theme==='glow') {
736             
737             cfg.tag = 'a';
738             cfg.cls = 'btn-glow roo-button';
739             
740             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
741                 
742                 cfg.cls += ' ' + this.weight;
743             }
744         }
745    
746         
747         if (this.inverse) {
748             this.cls += ' inverse';
749         }
750         
751         
752         if (this.active || this.pressed === true) {
753             cfg.cls += ' active';
754         }
755         
756         if (this.disabled) {
757             cfg.disabled = 'disabled';
758         }
759         
760         if (this.items) {
761             Roo.log('changing to ul' );
762             cfg.tag = 'ul';
763             this.glyphicon = 'caret';
764             if (Roo.bootstrap.version == 4) {
765                 this.fa = 'caret-down';
766             }
767             
768         }
769         
770         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
771          
772         //gsRoo.log(this.parentType);
773         if (this.parentType === 'Navbar' && !this.parent().bar) {
774             Roo.log('changing to li?');
775             
776             cfg.tag = 'li';
777             
778             cfg.cls = '';
779             cfg.cn =  [{
780                 tag : 'a',
781                 cls : 'roo-button',
782                 html : this.html,
783                 href : this.href || '#'
784             }];
785             if (this.menu) {
786                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
787                 cfg.cls += ' dropdown';
788             }   
789             
790             delete cfg.html;
791             
792         }
793         
794        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
795         
796         if (this.glyphicon) {
797             cfg.html = ' ' + cfg.html;
798             
799             cfg.cn = [
800                 {
801                     tag: 'span',
802                     cls: 'glyphicon glyphicon-' + this.glyphicon
803                 }
804             ];
805         }
806         if (this.fa) {
807             cfg.html = ' ' + cfg.html;
808             
809             cfg.cn = [
810                 {
811                     tag: 'i',
812                     cls: 'fa fas fa-' + this.fa
813                 }
814             ];
815         }
816         
817         if (this.badge) {
818             cfg.html += ' ';
819             
820             cfg.tag = 'a';
821             
822 //            cfg.cls='btn roo-button';
823             
824             cfg.href=this.href;
825             
826             var value = cfg.html;
827             
828             if(this.glyphicon){
829                 value = {
830                     tag: 'span',
831                     cls: 'glyphicon glyphicon-' + this.glyphicon,
832                     html: this.html
833                 };
834             }
835             if(this.fa){
836                 value = {
837                     tag: 'i',
838                     cls: 'fa fas fa-' + this.fa,
839                     html: this.html
840                 };
841             }
842             
843             var bw = this.badge_weight.length ? this.badge_weight :
844                 (this.weight.length ? this.weight : 'secondary');
845             bw = bw == 'default' ? 'secondary' : bw;
846             
847             cfg.cn = [
848                 value,
849                 {
850                     tag: 'span',
851                     cls: 'badge badge-' + bw,
852                     html: this.badge
853                 }
854             ];
855             
856             cfg.html='';
857         }
858         
859         if (this.menu) {
860             cfg.cls += ' dropdown';
861             cfg.html = typeof(cfg.html) != 'undefined' ?
862                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
863         }
864         
865         if (cfg.tag !== 'a' && this.href !== '') {
866             throw "Tag must be a to set href.";
867         } else if (this.href.length > 0) {
868             cfg.href = this.href;
869         }
870         
871         if(this.removeClass){
872             cfg.cls = '';
873         }
874         
875         if(this.target){
876             cfg.target = this.target;
877         }
878         
879         return cfg;
880     },
881     initEvents: function() {
882        // Roo.log('init events?');
883 //        Roo.log(this.el.dom);
884         // add the menu...
885         
886         if (typeof (this.menu) != 'undefined') {
887             this.menu.parentType = this.xtype;
888             this.menu.triggerEl = this.el;
889             this.addxtype(Roo.apply({}, this.menu));
890         }
891
892
893        if (this.el.hasClass('roo-button')) {
894             this.el.on('click', this.onClick, this);
895        } else {
896             this.el.select('.roo-button').on('click', this.onClick, this);
897        }
898        
899        if(this.removeClass){
900            this.el.on('click', this.onClick, this);
901        }
902        
903        this.el.enableDisplayMode();
904         
905     },
906     onClick : function(e)
907     {
908         if (this.disabled) {
909             return;
910         }
911         
912         Roo.log('button on click ');
913         if(this.preventDefault){
914             e.preventDefault();
915         }
916         
917         if (this.pressed === true || this.pressed === false) {
918             this.toggleActive(e);
919         }
920         
921         
922         this.fireEvent('click', this, e);
923     },
924     
925     /**
926      * Enables this button
927      */
928     enable : function()
929     {
930         this.disabled = false;
931         this.el.removeClass('disabled');
932     },
933     
934     /**
935      * Disable this button
936      */
937     disable : function()
938     {
939         this.disabled = true;
940         this.el.addClass('disabled');
941     },
942      /**
943      * sets the active state on/off, 
944      * @param {Boolean} state (optional) Force a particular state
945      */
946     setActive : function(v) {
947         
948         this.el[v ? 'addClass' : 'removeClass']('active');
949         this.pressed = v;
950     },
951      /**
952      * toggles the current active state 
953      */
954     toggleActive : function(e)
955     {
956         this.setActive(!this.pressed);
957         this.fireEvent('toggle', this, e, !this.pressed);
958     },
959      /**
960      * get the current active state
961      * @return {boolean} true if it's active
962      */
963     isActive : function()
964     {
965         return this.el.hasClass('active');
966     },
967     /**
968      * set the text of the first selected button
969      */
970     setText : function(str)
971     {
972         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
973     },
974     /**
975      * get the text of the first selected button
976      */
977     getText : function()
978     {
979         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
980     },
981     
982     setWeight : function(str)
983     {
984         this.el.removeClass(this.weightClass);
985         this.weight = str;
986         var outline = this.outline ? 'outline-' : '';
987         if (str == 'default') {
988             this.el.addClass('btn-default btn-outline-secondary');        
989             return;
990         }
991         this.el.addClass('btn-' + outline + str);        
992     }
993     
994     
995 });
996
997  /*
998  * - LGPL
999  *
1000  * column
1001  * 
1002  */
1003
1004 /**
1005  * @class Roo.bootstrap.Column
1006  * @extends Roo.bootstrap.Component
1007  * Bootstrap Column class
1008  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1009  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1010  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1011  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1012  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1013  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1014  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1015  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1016  *
1017  * 
1018  * @cfg {Boolean} hidden (true|false) hide the element
1019  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1020  * @cfg {String} fa (ban|check|...) font awesome icon
1021  * @cfg {Number} fasize (1|2|....) font awsome size
1022
1023  * @cfg {String} icon (info-sign|check|...) glyphicon name
1024
1025  * @cfg {String} html content of column.
1026  * 
1027  * @constructor
1028  * Create a new Column
1029  * @param {Object} config The config object
1030  */
1031
1032 Roo.bootstrap.Column = function(config){
1033     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1034 };
1035
1036 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1037     
1038     xs: false,
1039     sm: false,
1040     md: false,
1041     lg: false,
1042     xsoff: false,
1043     smoff: false,
1044     mdoff: false,
1045     lgoff: false,
1046     html: '',
1047     offset: 0,
1048     alert: false,
1049     fa: false,
1050     icon : false,
1051     hidden : false,
1052     fasize : 1,
1053     
1054     getAutoCreate : function(){
1055         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1056         
1057         cfg = {
1058             tag: 'div',
1059             cls: 'column'
1060         };
1061         
1062         var settings=this;
1063         ['xs','sm','md','lg'].map(function(size){
1064             //Roo.log( size + ':' + settings[size]);
1065             
1066             if (settings[size+'off'] !== false) {
1067                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1068             }
1069             
1070             if (settings[size] === false) {
1071                 return;
1072             }
1073             
1074             if (!settings[size]) { // 0 = hidden
1075                 cfg.cls += ' hidden-' + size;
1076                 return;
1077             }
1078             cfg.cls += ' col-' + size + '-' + settings[size];
1079             
1080         });
1081         
1082         if (this.hidden) {
1083             cfg.cls += ' hidden';
1084         }
1085         
1086         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1087             cfg.cls +=' alert alert-' + this.alert;
1088         }
1089         
1090         
1091         if (this.html.length) {
1092             cfg.html = this.html;
1093         }
1094         if (this.fa) {
1095             var fasize = '';
1096             if (this.fasize > 1) {
1097                 fasize = ' fa-' + this.fasize + 'x';
1098             }
1099             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1100             
1101             
1102         }
1103         if (this.icon) {
1104             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1105         }
1106         
1107         return cfg;
1108     }
1109    
1110 });
1111
1112  
1113
1114  /*
1115  * - LGPL
1116  *
1117  * page container.
1118  * 
1119  */
1120
1121
1122 /**
1123  * @class Roo.bootstrap.Container
1124  * @extends Roo.bootstrap.Component
1125  * Bootstrap Container class
1126  * @cfg {Boolean} jumbotron is it a jumbotron element
1127  * @cfg {String} html content of element
1128  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1129  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1130  * @cfg {String} header content of header (for panel)
1131  * @cfg {String} footer content of footer (for panel)
1132  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1133  * @cfg {String} tag (header|aside|section) type of HTML tag.
1134  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1135  * @cfg {String} fa font awesome icon
1136  * @cfg {String} icon (info-sign|check|...) glyphicon name
1137  * @cfg {Boolean} hidden (true|false) hide the element
1138  * @cfg {Boolean} expandable (true|false) default false
1139  * @cfg {Boolean} expanded (true|false) default true
1140  * @cfg {String} rheader contet on the right of header
1141  * @cfg {Boolean} clickable (true|false) default false
1142
1143  *     
1144  * @constructor
1145  * Create a new Container
1146  * @param {Object} config The config object
1147  */
1148
1149 Roo.bootstrap.Container = function(config){
1150     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1151     
1152     this.addEvents({
1153         // raw events
1154          /**
1155          * @event expand
1156          * After the panel has been expand
1157          * 
1158          * @param {Roo.bootstrap.Container} this
1159          */
1160         "expand" : true,
1161         /**
1162          * @event collapse
1163          * After the panel has been collapsed
1164          * 
1165          * @param {Roo.bootstrap.Container} this
1166          */
1167         "collapse" : true,
1168         /**
1169          * @event click
1170          * When a element is chick
1171          * @param {Roo.bootstrap.Container} this
1172          * @param {Roo.EventObject} e
1173          */
1174         "click" : true
1175     });
1176 };
1177
1178 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1179     
1180     jumbotron : false,
1181     well: '',
1182     panel : '',
1183     header: '',
1184     footer : '',
1185     sticky: '',
1186     tag : false,
1187     alert : false,
1188     fa: false,
1189     icon : false,
1190     expandable : false,
1191     rheader : '',
1192     expanded : true,
1193     clickable: false,
1194   
1195      
1196     getChildContainer : function() {
1197         
1198         if(!this.el){
1199             return false;
1200         }
1201         
1202         if (this.panel.length) {
1203             return this.el.select('.panel-body',true).first();
1204         }
1205         
1206         return this.el;
1207     },
1208     
1209     
1210     getAutoCreate : function(){
1211         
1212         var cfg = {
1213             tag : this.tag || 'div',
1214             html : '',
1215             cls : ''
1216         };
1217         if (this.jumbotron) {
1218             cfg.cls = 'jumbotron';
1219         }
1220         
1221         
1222         
1223         // - this is applied by the parent..
1224         //if (this.cls) {
1225         //    cfg.cls = this.cls + '';
1226         //}
1227         
1228         if (this.sticky.length) {
1229             
1230             var bd = Roo.get(document.body);
1231             if (!bd.hasClass('bootstrap-sticky')) {
1232                 bd.addClass('bootstrap-sticky');
1233                 Roo.select('html',true).setStyle('height', '100%');
1234             }
1235              
1236             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1237         }
1238         
1239         
1240         if (this.well.length) {
1241             switch (this.well) {
1242                 case 'lg':
1243                 case 'sm':
1244                     cfg.cls +=' well well-' +this.well;
1245                     break;
1246                 default:
1247                     cfg.cls +=' well';
1248                     break;
1249             }
1250         }
1251         
1252         if (this.hidden) {
1253             cfg.cls += ' hidden';
1254         }
1255         
1256         
1257         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1258             cfg.cls +=' alert alert-' + this.alert;
1259         }
1260         
1261         var body = cfg;
1262         
1263         if (this.panel.length) {
1264             cfg.cls += ' panel panel-' + this.panel;
1265             cfg.cn = [];
1266             if (this.header.length) {
1267                 
1268                 var h = [];
1269                 
1270                 if(this.expandable){
1271                     
1272                     cfg.cls = cfg.cls + ' expandable';
1273                     
1274                     h.push({
1275                         tag: 'i',
1276                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1277                     });
1278                     
1279                 }
1280                 
1281                 h.push(
1282                     {
1283                         tag: 'span',
1284                         cls : 'panel-title',
1285                         html : (this.expandable ? '&nbsp;' : '') + this.header
1286                     },
1287                     {
1288                         tag: 'span',
1289                         cls: 'panel-header-right',
1290                         html: this.rheader
1291                     }
1292                 );
1293                 
1294                 cfg.cn.push({
1295                     cls : 'panel-heading',
1296                     style : this.expandable ? 'cursor: pointer' : '',
1297                     cn : h
1298                 });
1299                 
1300             }
1301             
1302             body = false;
1303             cfg.cn.push({
1304                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1305                 html : this.html
1306             });
1307             
1308             
1309             if (this.footer.length) {
1310                 cfg.cn.push({
1311                     cls : 'panel-footer',
1312                     html : this.footer
1313                     
1314                 });
1315             }
1316             
1317         }
1318         
1319         if (body) {
1320             body.html = this.html || cfg.html;
1321             // prefix with the icons..
1322             if (this.fa) {
1323                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1324             }
1325             if (this.icon) {
1326                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1327             }
1328             
1329             
1330         }
1331         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1332             cfg.cls =  'container';
1333         }
1334         
1335         return cfg;
1336     },
1337     
1338     initEvents: function() 
1339     {
1340         if(this.expandable){
1341             var headerEl = this.headerEl();
1342         
1343             if(headerEl){
1344                 headerEl.on('click', this.onToggleClick, this);
1345             }
1346         }
1347         
1348         if(this.clickable){
1349             this.el.on('click', this.onClick, this);
1350         }
1351         
1352     },
1353     
1354     onToggleClick : function()
1355     {
1356         var headerEl = this.headerEl();
1357         
1358         if(!headerEl){
1359             return;
1360         }
1361         
1362         if(this.expanded){
1363             this.collapse();
1364             return;
1365         }
1366         
1367         this.expand();
1368     },
1369     
1370     expand : function()
1371     {
1372         if(this.fireEvent('expand', this)) {
1373             
1374             this.expanded = true;
1375             
1376             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1377             
1378             this.el.select('.panel-body',true).first().removeClass('hide');
1379             
1380             var toggleEl = this.toggleEl();
1381
1382             if(!toggleEl){
1383                 return;
1384             }
1385
1386             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1387         }
1388         
1389     },
1390     
1391     collapse : function()
1392     {
1393         if(this.fireEvent('collapse', this)) {
1394             
1395             this.expanded = false;
1396             
1397             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1398             this.el.select('.panel-body',true).first().addClass('hide');
1399         
1400             var toggleEl = this.toggleEl();
1401
1402             if(!toggleEl){
1403                 return;
1404             }
1405
1406             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1407         }
1408     },
1409     
1410     toggleEl : function()
1411     {
1412         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1413             return;
1414         }
1415         
1416         return this.el.select('.panel-heading .fa',true).first();
1417     },
1418     
1419     headerEl : function()
1420     {
1421         if(!this.el || !this.panel.length || !this.header.length){
1422             return;
1423         }
1424         
1425         return this.el.select('.panel-heading',true).first()
1426     },
1427     
1428     bodyEl : function()
1429     {
1430         if(!this.el || !this.panel.length){
1431             return;
1432         }
1433         
1434         return this.el.select('.panel-body',true).first()
1435     },
1436     
1437     titleEl : function()
1438     {
1439         if(!this.el || !this.panel.length || !this.header.length){
1440             return;
1441         }
1442         
1443         return this.el.select('.panel-title',true).first();
1444     },
1445     
1446     setTitle : function(v)
1447     {
1448         var titleEl = this.titleEl();
1449         
1450         if(!titleEl){
1451             return;
1452         }
1453         
1454         titleEl.dom.innerHTML = v;
1455     },
1456     
1457     getTitle : function()
1458     {
1459         
1460         var titleEl = this.titleEl();
1461         
1462         if(!titleEl){
1463             return '';
1464         }
1465         
1466         return titleEl.dom.innerHTML;
1467     },
1468     
1469     setRightTitle : function(v)
1470     {
1471         var t = this.el.select('.panel-header-right',true).first();
1472         
1473         if(!t){
1474             return;
1475         }
1476         
1477         t.dom.innerHTML = v;
1478     },
1479     
1480     onClick : function(e)
1481     {
1482         e.preventDefault();
1483         
1484         this.fireEvent('click', this, e);
1485     }
1486 });
1487
1488  /*
1489  * - LGPL
1490  *
1491  * image
1492  * 
1493  */
1494
1495
1496 /**
1497  * @class Roo.bootstrap.Img
1498  * @extends Roo.bootstrap.Component
1499  * Bootstrap Img class
1500  * @cfg {Boolean} imgResponsive false | true
1501  * @cfg {String} border rounded | circle | thumbnail
1502  * @cfg {String} src image source
1503  * @cfg {String} alt image alternative text
1504  * @cfg {String} href a tag href
1505  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1506  * @cfg {String} xsUrl xs image source
1507  * @cfg {String} smUrl sm image source
1508  * @cfg {String} mdUrl md image source
1509  * @cfg {String} lgUrl lg image source
1510  * 
1511  * @constructor
1512  * Create a new Input
1513  * @param {Object} config The config object
1514  */
1515
1516 Roo.bootstrap.Img = function(config){
1517     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1518     
1519     this.addEvents({
1520         // img events
1521         /**
1522          * @event click
1523          * The img click event for the img.
1524          * @param {Roo.EventObject} e
1525          */
1526         "click" : true
1527     });
1528 };
1529
1530 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1531     
1532     imgResponsive: true,
1533     border: '',
1534     src: 'about:blank',
1535     href: false,
1536     target: false,
1537     xsUrl: '',
1538     smUrl: '',
1539     mdUrl: '',
1540     lgUrl: '',
1541
1542     getAutoCreate : function()
1543     {   
1544         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1545             return this.createSingleImg();
1546         }
1547         
1548         var cfg = {
1549             tag: 'div',
1550             cls: 'roo-image-responsive-group',
1551             cn: []
1552         };
1553         var _this = this;
1554         
1555         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1556             
1557             if(!_this[size + 'Url']){
1558                 return;
1559             }
1560             
1561             var img = {
1562                 tag: 'img',
1563                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1564                 html: _this.html || cfg.html,
1565                 src: _this[size + 'Url']
1566             };
1567             
1568             img.cls += ' roo-image-responsive-' + size;
1569             
1570             var s = ['xs', 'sm', 'md', 'lg'];
1571             
1572             s.splice(s.indexOf(size), 1);
1573             
1574             Roo.each(s, function(ss){
1575                 img.cls += ' hidden-' + ss;
1576             });
1577             
1578             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1579                 cfg.cls += ' img-' + _this.border;
1580             }
1581             
1582             if(_this.alt){
1583                 cfg.alt = _this.alt;
1584             }
1585             
1586             if(_this.href){
1587                 var a = {
1588                     tag: 'a',
1589                     href: _this.href,
1590                     cn: [
1591                         img
1592                     ]
1593                 };
1594
1595                 if(this.target){
1596                     a.target = _this.target;
1597                 }
1598             }
1599             
1600             cfg.cn.push((_this.href) ? a : img);
1601             
1602         });
1603         
1604         return cfg;
1605     },
1606     
1607     createSingleImg : function()
1608     {
1609         var cfg = {
1610             tag: 'img',
1611             cls: (this.imgResponsive) ? 'img-responsive' : '',
1612             html : null,
1613             src : 'about:blank'  // just incase src get's set to undefined?!?
1614         };
1615         
1616         cfg.html = this.html || cfg.html;
1617         
1618         cfg.src = this.src || cfg.src;
1619         
1620         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1621             cfg.cls += ' img-' + this.border;
1622         }
1623         
1624         if(this.alt){
1625             cfg.alt = this.alt;
1626         }
1627         
1628         if(this.href){
1629             var a = {
1630                 tag: 'a',
1631                 href: this.href,
1632                 cn: [
1633                     cfg
1634                 ]
1635             };
1636             
1637             if(this.target){
1638                 a.target = this.target;
1639             }
1640             
1641         }
1642         
1643         return (this.href) ? a : cfg;
1644     },
1645     
1646     initEvents: function() 
1647     {
1648         if(!this.href){
1649             this.el.on('click', this.onClick, this);
1650         }
1651         
1652     },
1653     
1654     onClick : function(e)
1655     {
1656         Roo.log('img onclick');
1657         this.fireEvent('click', this, e);
1658     },
1659     /**
1660      * Sets the url of the image - used to update it
1661      * @param {String} url the url of the image
1662      */
1663     
1664     setSrc : function(url)
1665     {
1666         this.src =  url;
1667         
1668         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1669             this.el.dom.src =  url;
1670             return;
1671         }
1672         
1673         this.el.select('img', true).first().dom.src =  url;
1674     }
1675     
1676     
1677    
1678 });
1679
1680  /*
1681  * - LGPL
1682  *
1683  * image
1684  * 
1685  */
1686
1687
1688 /**
1689  * @class Roo.bootstrap.Link
1690  * @extends Roo.bootstrap.Component
1691  * Bootstrap Link Class
1692  * @cfg {String} alt image alternative text
1693  * @cfg {String} href a tag href
1694  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1695  * @cfg {String} html the content of the link.
1696  * @cfg {String} anchor name for the anchor link
1697  * @cfg {String} fa - favicon
1698
1699  * @cfg {Boolean} preventDefault (true | false) default false
1700
1701  * 
1702  * @constructor
1703  * Create a new Input
1704  * @param {Object} config The config object
1705  */
1706
1707 Roo.bootstrap.Link = function(config){
1708     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1709     
1710     this.addEvents({
1711         // img events
1712         /**
1713          * @event click
1714          * The img click event for the img.
1715          * @param {Roo.EventObject} e
1716          */
1717         "click" : true
1718     });
1719 };
1720
1721 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1722     
1723     href: false,
1724     target: false,
1725     preventDefault: false,
1726     anchor : false,
1727     alt : false,
1728     fa: false,
1729
1730
1731     getAutoCreate : function()
1732     {
1733         var html = this.html || '';
1734         
1735         if (this.fa !== false) {
1736             html = '<i class="fa fa-' + this.fa + '"></i>';
1737         }
1738         var cfg = {
1739             tag: 'a'
1740         };
1741         // anchor's do not require html/href...
1742         if (this.anchor === false) {
1743             cfg.html = html;
1744             cfg.href = this.href || '#';
1745         } else {
1746             cfg.name = this.anchor;
1747             if (this.html !== false || this.fa !== false) {
1748                 cfg.html = html;
1749             }
1750             if (this.href !== false) {
1751                 cfg.href = this.href;
1752             }
1753         }
1754         
1755         if(this.alt !== false){
1756             cfg.alt = this.alt;
1757         }
1758         
1759         
1760         if(this.target !== false) {
1761             cfg.target = this.target;
1762         }
1763         
1764         return cfg;
1765     },
1766     
1767     initEvents: function() {
1768         
1769         if(!this.href || this.preventDefault){
1770             this.el.on('click', this.onClick, this);
1771         }
1772     },
1773     
1774     onClick : function(e)
1775     {
1776         if(this.preventDefault){
1777             e.preventDefault();
1778         }
1779         //Roo.log('img onclick');
1780         this.fireEvent('click', this, e);
1781     }
1782    
1783 });
1784
1785  /*
1786  * - LGPL
1787  *
1788  * header
1789  * 
1790  */
1791
1792 /**
1793  * @class Roo.bootstrap.Header
1794  * @extends Roo.bootstrap.Component
1795  * Bootstrap Header class
1796  * @cfg {String} html content of header
1797  * @cfg {Number} level (1|2|3|4|5|6) default 1
1798  * 
1799  * @constructor
1800  * Create a new Header
1801  * @param {Object} config The config object
1802  */
1803
1804
1805 Roo.bootstrap.Header  = function(config){
1806     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1807 };
1808
1809 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1810     
1811     //href : false,
1812     html : false,
1813     level : 1,
1814     
1815     
1816     
1817     getAutoCreate : function(){
1818         
1819         
1820         
1821         var cfg = {
1822             tag: 'h' + (1 *this.level),
1823             html: this.html || ''
1824         } ;
1825         
1826         return cfg;
1827     }
1828    
1829 });
1830
1831  
1832
1833  /*
1834  * Based on:
1835  * Ext JS Library 1.1.1
1836  * Copyright(c) 2006-2007, Ext JS, LLC.
1837  *
1838  * Originally Released Under LGPL - original licence link has changed is not relivant.
1839  *
1840  * Fork - LGPL
1841  * <script type="text/javascript">
1842  */
1843  
1844 /**
1845  * @class Roo.bootstrap.MenuMgr
1846  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1847  * @singleton
1848  */
1849 Roo.bootstrap.MenuMgr = function(){
1850    var menus, active, groups = {}, attached = false, lastShow = new Date();
1851
1852    // private - called when first menu is created
1853    function init(){
1854        menus = {};
1855        active = new Roo.util.MixedCollection();
1856        Roo.get(document).addKeyListener(27, function(){
1857            if(active.length > 0){
1858                hideAll();
1859            }
1860        });
1861    }
1862
1863    // private
1864    function hideAll(){
1865        if(active && active.length > 0){
1866            var c = active.clone();
1867            c.each(function(m){
1868                m.hide();
1869            });
1870        }
1871    }
1872
1873    // private
1874    function onHide(m){
1875        active.remove(m);
1876        if(active.length < 1){
1877            Roo.get(document).un("mouseup", onMouseDown);
1878             
1879            attached = false;
1880        }
1881    }
1882
1883    // private
1884    function onShow(m){
1885        var last = active.last();
1886        lastShow = new Date();
1887        active.add(m);
1888        if(!attached){
1889           Roo.get(document).on("mouseup", onMouseDown);
1890            
1891            attached = true;
1892        }
1893        if(m.parentMenu){
1894           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1895           m.parentMenu.activeChild = m;
1896        }else if(last && last.isVisible()){
1897           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1898        }
1899    }
1900
1901    // private
1902    function onBeforeHide(m){
1903        if(m.activeChild){
1904            m.activeChild.hide();
1905        }
1906        if(m.autoHideTimer){
1907            clearTimeout(m.autoHideTimer);
1908            delete m.autoHideTimer;
1909        }
1910    }
1911
1912    // private
1913    function onBeforeShow(m){
1914        var pm = m.parentMenu;
1915        if(!pm && !m.allowOtherMenus){
1916            hideAll();
1917        }else if(pm && pm.activeChild && active != m){
1918            pm.activeChild.hide();
1919        }
1920    }
1921
1922    // private this should really trigger on mouseup..
1923    function onMouseDown(e){
1924         Roo.log("on Mouse Up");
1925         
1926         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1927             Roo.log("MenuManager hideAll");
1928             hideAll();
1929             e.stopEvent();
1930         }
1931         
1932         
1933    }
1934
1935    // private
1936    function onBeforeCheck(mi, state){
1937        if(state){
1938            var g = groups[mi.group];
1939            for(var i = 0, l = g.length; i < l; i++){
1940                if(g[i] != mi){
1941                    g[i].setChecked(false);
1942                }
1943            }
1944        }
1945    }
1946
1947    return {
1948
1949        /**
1950         * Hides all menus that are currently visible
1951         */
1952        hideAll : function(){
1953             hideAll();  
1954        },
1955
1956        // private
1957        register : function(menu){
1958            if(!menus){
1959                init();
1960            }
1961            menus[menu.id] = menu;
1962            menu.on("beforehide", onBeforeHide);
1963            menu.on("hide", onHide);
1964            menu.on("beforeshow", onBeforeShow);
1965            menu.on("show", onShow);
1966            var g = menu.group;
1967            if(g && menu.events["checkchange"]){
1968                if(!groups[g]){
1969                    groups[g] = [];
1970                }
1971                groups[g].push(menu);
1972                menu.on("checkchange", onCheck);
1973            }
1974        },
1975
1976         /**
1977          * Returns a {@link Roo.menu.Menu} object
1978          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1979          * be used to generate and return a new Menu instance.
1980          */
1981        get : function(menu){
1982            if(typeof menu == "string"){ // menu id
1983                return menus[menu];
1984            }else if(menu.events){  // menu instance
1985                return menu;
1986            }
1987            /*else if(typeof menu.length == 'number'){ // array of menu items?
1988                return new Roo.bootstrap.Menu({items:menu});
1989            }else{ // otherwise, must be a config
1990                return new Roo.bootstrap.Menu(menu);
1991            }
1992            */
1993            return false;
1994        },
1995
1996        // private
1997        unregister : function(menu){
1998            delete menus[menu.id];
1999            menu.un("beforehide", onBeforeHide);
2000            menu.un("hide", onHide);
2001            menu.un("beforeshow", onBeforeShow);
2002            menu.un("show", onShow);
2003            var g = menu.group;
2004            if(g && menu.events["checkchange"]){
2005                groups[g].remove(menu);
2006                menu.un("checkchange", onCheck);
2007            }
2008        },
2009
2010        // private
2011        registerCheckable : function(menuItem){
2012            var g = menuItem.group;
2013            if(g){
2014                if(!groups[g]){
2015                    groups[g] = [];
2016                }
2017                groups[g].push(menuItem);
2018                menuItem.on("beforecheckchange", onBeforeCheck);
2019            }
2020        },
2021
2022        // private
2023        unregisterCheckable : function(menuItem){
2024            var g = menuItem.group;
2025            if(g){
2026                groups[g].remove(menuItem);
2027                menuItem.un("beforecheckchange", onBeforeCheck);
2028            }
2029        }
2030    };
2031 }();/*
2032  * - LGPL
2033  *
2034  * menu
2035  * 
2036  */
2037
2038 /**
2039  * @class Roo.bootstrap.Menu
2040  * @extends Roo.bootstrap.Component
2041  * Bootstrap Menu class - container for MenuItems
2042  * @cfg {String} type (dropdown|treeview|submenu) type of menu
2043  * @cfg {bool} hidden  if the menu should be hidden when rendered.
2044  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
2045  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
2046  * 
2047  * @constructor
2048  * Create a new Menu
2049  * @param {Object} config The config object
2050  */
2051
2052
2053 Roo.bootstrap.Menu = function(config){
2054     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
2055     if (this.registerMenu && this.type != 'treeview')  {
2056         Roo.bootstrap.MenuMgr.register(this);
2057     }
2058     
2059     
2060     this.addEvents({
2061         /**
2062          * @event beforeshow
2063          * Fires before this menu is displayed
2064          * @param {Roo.menu.Menu} this
2065          */
2066         beforeshow : true,
2067         /**
2068          * @event beforehide
2069          * Fires before this menu is hidden
2070          * @param {Roo.menu.Menu} this
2071          */
2072         beforehide : true,
2073         /**
2074          * @event show
2075          * Fires after this menu is displayed
2076          * @param {Roo.menu.Menu} this
2077          */
2078         show : true,
2079         /**
2080          * @event hide
2081          * Fires after this menu is hidden
2082          * @param {Roo.menu.Menu} this
2083          */
2084         hide : true,
2085         /**
2086          * @event click
2087          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
2088          * @param {Roo.menu.Menu} this
2089          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2090          * @param {Roo.EventObject} e
2091          */
2092         click : true,
2093         /**
2094          * @event mouseover
2095          * Fires when the mouse is hovering over this menu
2096          * @param {Roo.menu.Menu} this
2097          * @param {Roo.EventObject} e
2098          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2099          */
2100         mouseover : true,
2101         /**
2102          * @event mouseout
2103          * Fires when the mouse exits this menu
2104          * @param {Roo.menu.Menu} this
2105          * @param {Roo.EventObject} e
2106          * @param {Roo.menu.Item} menuItem The menu item that was clicked
2107          */
2108         mouseout : true,
2109         /**
2110          * @event itemclick
2111          * Fires when a menu item contained in this menu is clicked
2112          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2113          * @param {Roo.EventObject} e
2114          */
2115         itemclick: true
2116     });
2117     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2118 };
2119
2120 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2121     
2122    /// html : false,
2123     //align : '',
2124     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2125     type: false,
2126     /**
2127      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2128      */
2129     registerMenu : true,
2130     
2131     menuItems :false, // stores the menu items..
2132     
2133     hidden:true,
2134         
2135     parentMenu : false,
2136     
2137     stopEvent : true,
2138     
2139     isLink : false,
2140     
2141     getChildContainer : function() {
2142         return this.el;  
2143     },
2144     
2145     getAutoCreate : function(){
2146          
2147         //if (['right'].indexOf(this.align)!==-1) {
2148         //    cfg.cn[1].cls += ' pull-right'
2149         //}
2150         
2151         
2152         var cfg = {
2153             tag : 'ul',
2154             cls : 'dropdown-menu' ,
2155             style : 'z-index:1000'
2156             
2157         };
2158         
2159         if (this.type === 'submenu') {
2160             cfg.cls = 'submenu active';
2161         }
2162         if (this.type === 'treeview') {
2163             cfg.cls = 'treeview-menu';
2164         }
2165         
2166         return cfg;
2167     },
2168     initEvents : function() {
2169         
2170        // Roo.log("ADD event");
2171        // Roo.log(this.triggerEl.dom);
2172         
2173         this.triggerEl.on('click', this.onTriggerClick, this);
2174         
2175         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2176         
2177         
2178         if (this.triggerEl.hasClass('nav-item')) {
2179             // dropdown toggle on the 'a' in BS4?
2180             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
2181         } else {
2182             this.triggerEl.addClass('dropdown-toggle');
2183         }
2184         if (Roo.isTouch) {
2185             this.el.on('touchstart'  , this.onTouch, this);
2186         }
2187         this.el.on('click' , this.onClick, this);
2188
2189         this.el.on("mouseover", this.onMouseOver, this);
2190         this.el.on("mouseout", this.onMouseOut, this);
2191         
2192     },
2193     
2194     findTargetItem : function(e)
2195     {
2196         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2197         if(!t){
2198             return false;
2199         }
2200         //Roo.log(t);         Roo.log(t.id);
2201         if(t && t.id){
2202             //Roo.log(this.menuitems);
2203             return this.menuitems.get(t.id);
2204             
2205             //return this.items.get(t.menuItemId);
2206         }
2207         
2208         return false;
2209     },
2210     
2211     onTouch : function(e) 
2212     {
2213         Roo.log("menu.onTouch");
2214         //e.stopEvent(); this make the user popdown broken
2215         this.onClick(e);
2216     },
2217     
2218     onClick : function(e)
2219     {
2220         Roo.log("menu.onClick");
2221         
2222         var t = this.findTargetItem(e);
2223         if(!t || t.isContainer){
2224             return;
2225         }
2226         Roo.log(e);
2227         /*
2228         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2229             if(t == this.activeItem && t.shouldDeactivate(e)){
2230                 this.activeItem.deactivate();
2231                 delete this.activeItem;
2232                 return;
2233             }
2234             if(t.canActivate){
2235                 this.setActiveItem(t, true);
2236             }
2237             return;
2238             
2239             
2240         }
2241         */
2242        
2243         Roo.log('pass click event');
2244         
2245         t.onClick(e);
2246         
2247         this.fireEvent("click", this, t, e);
2248         
2249         var _this = this;
2250         
2251         if(!t.href.length || t.href == '#'){
2252             (function() { _this.hide(); }).defer(100);
2253         }
2254         
2255     },
2256     
2257     onMouseOver : function(e){
2258         var t  = this.findTargetItem(e);
2259         //Roo.log(t);
2260         //if(t){
2261         //    if(t.canActivate && !t.disabled){
2262         //        this.setActiveItem(t, true);
2263         //    }
2264         //}
2265         
2266         this.fireEvent("mouseover", this, e, t);
2267     },
2268     isVisible : function(){
2269         return !this.hidden;
2270     },
2271      onMouseOut : function(e){
2272         var t  = this.findTargetItem(e);
2273         
2274         //if(t ){
2275         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2276         //        this.activeItem.deactivate();
2277         //        delete this.activeItem;
2278         //    }
2279         //}
2280         this.fireEvent("mouseout", this, e, t);
2281     },
2282     
2283     
2284     /**
2285      * Displays this menu relative to another element
2286      * @param {String/HTMLElement/Roo.Element} element The element to align to
2287      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2288      * the element (defaults to this.defaultAlign)
2289      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2290      */
2291     show : function(el, pos, parentMenu){
2292         this.parentMenu = parentMenu;
2293         if(!this.el){
2294             this.render();
2295         }
2296         this.fireEvent("beforeshow", this);
2297         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2298     },
2299      /**
2300      * Displays this menu at a specific xy position
2301      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2302      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2303      */
2304     showAt : function(xy, parentMenu, /* private: */_e){
2305         this.parentMenu = parentMenu;
2306         if(!this.el){
2307             this.render();
2308         }
2309         if(_e !== false){
2310             this.fireEvent("beforeshow", this);
2311             //xy = this.el.adjustForConstraints(xy);
2312         }
2313         
2314         //this.el.show();
2315         this.hideMenuItems();
2316         this.hidden = false;
2317         this.triggerEl.addClass('open');
2318         this.el.addClass('show');
2319         
2320         // reassign x when hitting right
2321         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
2322             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2323         }
2324         
2325         // reassign y when hitting bottom
2326         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
2327             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
2328         }
2329         
2330         // but the list may align on trigger left or trigger top... should it be a properity?
2331         
2332         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2333             this.el.setXY(xy);
2334         }
2335         
2336         this.focus();
2337         this.fireEvent("show", this);
2338     },
2339     
2340     focus : function(){
2341         return;
2342         if(!this.hidden){
2343             this.doFocus.defer(50, this);
2344         }
2345     },
2346
2347     doFocus : function(){
2348         if(!this.hidden){
2349             this.focusEl.focus();
2350         }
2351     },
2352
2353     /**
2354      * Hides this menu and optionally all parent menus
2355      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2356      */
2357     hide : function(deep)
2358     {
2359         
2360         this.hideMenuItems();
2361         if(this.el && this.isVisible()){
2362             this.fireEvent("beforehide", this);
2363             if(this.activeItem){
2364                 this.activeItem.deactivate();
2365                 this.activeItem = null;
2366             }
2367             this.triggerEl.removeClass('open');;
2368             this.el.removeClass('show');
2369             this.hidden = true;
2370             this.fireEvent("hide", this);
2371         }
2372         if(deep === true && this.parentMenu){
2373             this.parentMenu.hide(true);
2374         }
2375     },
2376     
2377     onTriggerClick : function(e)
2378     {
2379         Roo.log('trigger click');
2380         
2381         var target = e.getTarget();
2382         
2383         Roo.log(target.nodeName.toLowerCase());
2384         
2385         if(target.nodeName.toLowerCase() === 'i'){
2386             e.preventDefault();
2387         }
2388         
2389     },
2390     
2391     onTriggerPress  : function(e)
2392     {
2393         Roo.log('trigger press');
2394         //Roo.log(e.getTarget());
2395        // Roo.log(this.triggerEl.dom);
2396        
2397         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2398         var pel = Roo.get(e.getTarget());
2399         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2400             Roo.log('is treeview or dropdown?');
2401             return;
2402         }
2403         
2404         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2405             return;
2406         }
2407         
2408         if (this.isVisible()) {
2409             Roo.log('hide');
2410             this.hide();
2411         } else {
2412             Roo.log('show');
2413             this.show(this.triggerEl, '?', false);
2414         }
2415         
2416         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2417             e.stopEvent();
2418         }
2419         
2420     },
2421        
2422     
2423     hideMenuItems : function()
2424     {
2425         Roo.log("hide Menu Items");
2426         if (!this.el) { 
2427             return;
2428         }
2429         //$(backdrop).remove()
2430         this.el.select('.open',true).each(function(aa) {
2431             
2432             aa.removeClass('open');
2433           //var parent = getParent($(this))
2434           //var relatedTarget = { relatedTarget: this }
2435           
2436            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2437           //if (e.isDefaultPrevented()) return
2438            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2439         });
2440     },
2441     addxtypeChild : function (tree, cntr) {
2442         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2443           
2444         this.menuitems.add(comp);
2445         return comp;
2446
2447     },
2448     getEl : function()
2449     {
2450         Roo.log(this.el);
2451         return this.el;
2452     },
2453     
2454     clear : function()
2455     {
2456         this.getEl().dom.innerHTML = '';
2457         this.menuitems.clear();
2458     }
2459 });
2460
2461  
2462  /*
2463  * - LGPL
2464  *
2465  * menu item
2466  * 
2467  */
2468
2469
2470 /**
2471  * @class Roo.bootstrap.MenuItem
2472  * @extends Roo.bootstrap.Component
2473  * Bootstrap MenuItem class
2474  * @cfg {String} html the menu label
2475  * @cfg {String} href the link
2476  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2477  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2478  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2479  * @cfg {String} fa favicon to show on left of menu item.
2480  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2481  * 
2482  * 
2483  * @constructor
2484  * Create a new MenuItem
2485  * @param {Object} config The config object
2486  */
2487
2488
2489 Roo.bootstrap.MenuItem = function(config){
2490     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2491     this.addEvents({
2492         // raw events
2493         /**
2494          * @event click
2495          * The raw click event for the entire grid.
2496          * @param {Roo.bootstrap.MenuItem} this
2497          * @param {Roo.EventObject} e
2498          */
2499         "click" : true
2500     });
2501 };
2502
2503 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2504     
2505     href : false,
2506     html : false,
2507     preventDefault: false,
2508     isContainer : false,
2509     active : false,
2510     fa: false,
2511     
2512     getAutoCreate : function(){
2513         
2514         if(this.isContainer){
2515             return {
2516                 tag: 'li',
2517                 cls: 'dropdown-menu-item '
2518             };
2519         }
2520         var ctag = {
2521             tag: 'span',
2522             html: 'Link'
2523         };
2524         
2525         var anc = {
2526             tag : 'a',
2527             cls : 'dropdown-item',
2528             href : '#',
2529             cn : [  ]
2530         };
2531         
2532         if (this.fa !== false) {
2533             anc.cn.push({
2534                 tag : 'i',
2535                 cls : 'fa fa-' + this.fa
2536             });
2537         }
2538         
2539         anc.cn.push(ctag);
2540         
2541         
2542         var cfg= {
2543             tag: 'li',
2544             cls: 'dropdown-menu-item',
2545             cn: [ anc ]
2546         };
2547         if (this.parent().type == 'treeview') {
2548             cfg.cls = 'treeview-menu';
2549         }
2550         if (this.active) {
2551             cfg.cls += ' active';
2552         }
2553         
2554         
2555         
2556         anc.href = this.href || cfg.cn[0].href ;
2557         ctag.html = this.html || cfg.cn[0].html ;
2558         return cfg;
2559     },
2560     
2561     initEvents: function()
2562     {
2563         if (this.parent().type == 'treeview') {
2564             this.el.select('a').on('click', this.onClick, this);
2565         }
2566         
2567         if (this.menu) {
2568             this.menu.parentType = this.xtype;
2569             this.menu.triggerEl = this.el;
2570             this.menu = this.addxtype(Roo.apply({}, this.menu));
2571         }
2572         
2573     },
2574     onClick : function(e)
2575     {
2576         Roo.log('item on click ');
2577         
2578         if(this.preventDefault){
2579             e.preventDefault();
2580         }
2581         //this.parent().hideMenuItems();
2582         
2583         this.fireEvent('click', this, e);
2584     },
2585     getEl : function()
2586     {
2587         return this.el;
2588     } 
2589 });
2590
2591  
2592
2593  /*
2594  * - LGPL
2595  *
2596  * menu separator
2597  * 
2598  */
2599
2600
2601 /**
2602  * @class Roo.bootstrap.MenuSeparator
2603  * @extends Roo.bootstrap.Component
2604  * Bootstrap MenuSeparator class
2605  * 
2606  * @constructor
2607  * Create a new MenuItem
2608  * @param {Object} config The config object
2609  */
2610
2611
2612 Roo.bootstrap.MenuSeparator = function(config){
2613     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2614 };
2615
2616 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2617     
2618     getAutoCreate : function(){
2619         var cfg = {
2620             cls: 'divider',
2621             tag : 'li'
2622         };
2623         
2624         return cfg;
2625     }
2626    
2627 });
2628
2629  
2630
2631  
2632 /*
2633 * Licence: LGPL
2634 */
2635
2636 /**
2637  * @class Roo.bootstrap.Modal
2638  * @extends Roo.bootstrap.Component
2639  * Bootstrap Modal class
2640  * @cfg {String} title Title of dialog
2641  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2642  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2643  * @cfg {Boolean} specificTitle default false
2644  * @cfg {Array} buttons Array of buttons or standard button set..
2645  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
2646  * @cfg {Boolean} animate default true
2647  * @cfg {Boolean} allow_close default true
2648  * @cfg {Boolean} fitwindow default false
2649  * @cfg {String} size (sm|lg) default empty
2650  * @cfg {Number} max_width set the max width of modal
2651  *
2652  *
2653  * @constructor
2654  * Create a new Modal Dialog
2655  * @param {Object} config The config object
2656  */
2657
2658 Roo.bootstrap.Modal = function(config){
2659     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2660     this.addEvents({
2661         // raw events
2662         /**
2663          * @event btnclick
2664          * The raw btnclick event for the button
2665          * @param {Roo.EventObject} e
2666          */
2667         "btnclick" : true,
2668         /**
2669          * @event resize
2670          * Fire when dialog resize
2671          * @param {Roo.bootstrap.Modal} this
2672          * @param {Roo.EventObject} e
2673          */
2674         "resize" : true
2675     });
2676     this.buttons = this.buttons || [];
2677
2678     if (this.tmpl) {
2679         this.tmpl = Roo.factory(this.tmpl);
2680     }
2681
2682 };
2683
2684 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2685
2686     title : 'test dialog',
2687
2688     buttons : false,
2689
2690     // set on load...
2691
2692     html: false,
2693
2694     tmp: false,
2695
2696     specificTitle: false,
2697
2698     buttonPosition: 'right',
2699
2700     allow_close : true,
2701
2702     animate : true,
2703
2704     fitwindow: false,
2705     
2706      // private
2707     dialogEl: false,
2708     bodyEl:  false,
2709     footerEl:  false,
2710     titleEl:  false,
2711     closeEl:  false,
2712
2713     size: '',
2714     
2715     max_width: 0,
2716     
2717     max_height: 0,
2718     
2719     fit_content: false,
2720
2721     onRender : function(ct, position)
2722     {
2723         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2724
2725         if(!this.el){
2726             var cfg = Roo.apply({},  this.getAutoCreate());
2727             cfg.id = Roo.id();
2728             //if(!cfg.name){
2729             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2730             //}
2731             //if (!cfg.name.length) {
2732             //    delete cfg.name;
2733            // }
2734             if (this.cls) {
2735                 cfg.cls += ' ' + this.cls;
2736             }
2737             if (this.style) {
2738                 cfg.style = this.style;
2739             }
2740             this.el = Roo.get(document.body).createChild(cfg, position);
2741         }
2742         //var type = this.el.dom.type;
2743
2744
2745         if(this.tabIndex !== undefined){
2746             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2747         }
2748
2749         this.dialogEl = this.el.select('.modal-dialog',true).first();
2750         this.bodyEl = this.el.select('.modal-body',true).first();
2751         this.closeEl = this.el.select('.modal-header .close', true).first();
2752         this.headerEl = this.el.select('.modal-header',true).first();
2753         this.titleEl = this.el.select('.modal-title',true).first();
2754         this.footerEl = this.el.select('.modal-footer',true).first();
2755
2756         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2757         
2758         //this.el.addClass("x-dlg-modal");
2759
2760         if (this.buttons.length) {
2761             Roo.each(this.buttons, function(bb) {
2762                 var b = Roo.apply({}, bb);
2763                 b.xns = b.xns || Roo.bootstrap;
2764                 b.xtype = b.xtype || 'Button';
2765                 if (typeof(b.listeners) == 'undefined') {
2766                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2767                 }
2768
2769                 var btn = Roo.factory(b);
2770
2771                 btn.render(this.getButtonContainer());
2772
2773             },this);
2774         }
2775         // render the children.
2776         var nitems = [];
2777
2778         if(typeof(this.items) != 'undefined'){
2779             var items = this.items;
2780             delete this.items;
2781
2782             for(var i =0;i < items.length;i++) {
2783                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2784             }
2785         }
2786
2787         this.items = nitems;
2788
2789         // where are these used - they used to be body/close/footer
2790
2791
2792         this.initEvents();
2793         //this.el.addClass([this.fieldClass, this.cls]);
2794
2795     },
2796
2797     getAutoCreate : function()
2798     {
2799         var bdy = {
2800                 cls : 'modal-body',
2801                 html : this.html || ''
2802         };
2803
2804         var title = {
2805             tag: 'h4',
2806             cls : 'modal-title',
2807             html : this.title
2808         };
2809
2810         if(this.specificTitle){
2811             title = this.title;
2812
2813         }
2814
2815         var header = [];
2816         if (this.allow_close && Roo.bootstrap.version == 3) {
2817             header.push({
2818                 tag: 'button',
2819                 cls : 'close',
2820                 html : '&times'
2821             });
2822         }
2823
2824         header.push(title);
2825
2826         if (this.allow_close && Roo.bootstrap.version == 4) {
2827             header.push({
2828                 tag: 'button',
2829                 cls : 'close',
2830                 html : '&times'
2831             });
2832         }
2833         
2834         var size = '';
2835
2836         if(this.size.length){
2837             size = 'modal-' + this.size;
2838         }
2839         
2840         var footer = Roo.bootstrap.version == 3 ?
2841             {
2842                 cls : 'modal-footer',
2843                 cn : [
2844                     {
2845                         tag: 'div',
2846                         cls: 'btn-' + this.buttonPosition
2847                     }
2848                 ]
2849
2850             } :
2851             {  // BS4 uses mr-auto on left buttons....
2852                 cls : 'modal-footer'
2853             };
2854
2855             
2856
2857         
2858         
2859         var modal = {
2860             cls: "modal",
2861              cn : [
2862                 {
2863                     cls: "modal-dialog " + size,
2864                     cn : [
2865                         {
2866                             cls : "modal-content",
2867                             cn : [
2868                                 {
2869                                     cls : 'modal-header',
2870                                     cn : header
2871                                 },
2872                                 bdy,
2873                                 footer
2874                             ]
2875
2876                         }
2877                     ]
2878
2879                 }
2880             ]
2881         };
2882
2883         if(this.animate){
2884             modal.cls += ' fade';
2885         }
2886
2887         return modal;
2888
2889     },
2890     getChildContainer : function() {
2891
2892          return this.bodyEl;
2893
2894     },
2895     getButtonContainer : function() {
2896         
2897          return Roo.bootstrap.version == 4 ?
2898             this.el.select('.modal-footer',true).first()
2899             : this.el.select('.modal-footer div',true).first();
2900
2901     },
2902     initEvents : function()
2903     {
2904         if (this.allow_close) {
2905             this.closeEl.on('click', this.hide, this);
2906         }
2907         Roo.EventManager.onWindowResize(this.resize, this, true);
2908
2909
2910     },
2911   
2912
2913     resize : function()
2914     {
2915         this.maskEl.setSize(
2916             Roo.lib.Dom.getViewWidth(true),
2917             Roo.lib.Dom.getViewHeight(true)
2918         );
2919         
2920         if (this.fitwindow) {
2921             
2922            
2923             this.setSize(
2924                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
2925                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
2926             );
2927             return;
2928         }
2929         
2930         if(this.max_width !== 0) {
2931             
2932             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
2933             
2934             if(this.height) {
2935                 this.setSize(w, this.height);
2936                 return;
2937             }
2938             
2939             if(this.max_height) {
2940                 this.setSize(w,Math.min(
2941                     this.max_height,
2942                     Roo.lib.Dom.getViewportHeight(true) - 60
2943                 ));
2944                 
2945                 return;
2946             }
2947             
2948             if(!this.fit_content) {
2949                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
2950                 return;
2951             }
2952             
2953             this.setSize(w, Math.min(
2954                 60 +
2955                 this.headerEl.getHeight() + 
2956                 this.footerEl.getHeight() + 
2957                 this.getChildHeight(this.bodyEl.dom.childNodes),
2958                 Roo.lib.Dom.getViewportHeight(true) - 60)
2959             );
2960         }
2961         
2962     },
2963
2964     setSize : function(w,h)
2965     {
2966         if (!w && !h) {
2967             return;
2968         }
2969         
2970         this.resizeTo(w,h);
2971     },
2972
2973     show : function() {
2974
2975         if (!this.rendered) {
2976             this.render();
2977         }
2978
2979         //this.el.setStyle('display', 'block');
2980         this.el.removeClass('hideing');
2981         this.el.dom.style.display='block';
2982         
2983         Roo.get(document.body).addClass('modal-open');
2984  
2985         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2986             
2987             (function(){
2988                 this.el.addClass('show');
2989                 this.el.addClass('in');
2990             }).defer(50, this);
2991         }else{
2992             this.el.addClass('show');
2993             this.el.addClass('in');
2994         }
2995
2996         // not sure how we can show data in here..
2997         //if (this.tmpl) {
2998         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2999         //}
3000
3001         Roo.get(document.body).addClass("x-body-masked");
3002         
3003         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
3004         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3005         this.maskEl.dom.style.display = 'block';
3006         this.maskEl.addClass('show');
3007         
3008         
3009         this.resize();
3010         
3011         this.fireEvent('show', this);
3012
3013         // set zindex here - otherwise it appears to be ignored...
3014         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
3015
3016         (function () {
3017             this.items.forEach( function(e) {
3018                 e.layout ? e.layout() : false;
3019
3020             });
3021         }).defer(100,this);
3022
3023     },
3024     hide : function()
3025     {
3026         if(this.fireEvent("beforehide", this) !== false){
3027             
3028             this.maskEl.removeClass('show');
3029             
3030             this.maskEl.dom.style.display = '';
3031             Roo.get(document.body).removeClass("x-body-masked");
3032             this.el.removeClass('in');
3033             this.el.select('.modal-dialog', true).first().setStyle('transform','');
3034
3035             if(this.animate){ // why
3036                 this.el.addClass('hideing');
3037                 this.el.removeClass('show');
3038                 (function(){
3039                     if (!this.el.hasClass('hideing')) {
3040                         return; // it's been shown again...
3041                     }
3042                     
3043                     this.el.dom.style.display='';
3044
3045                     Roo.get(document.body).removeClass('modal-open');
3046                     this.el.removeClass('hideing');
3047                 }).defer(150,this);
3048                 
3049             }else{
3050                 this.el.removeClass('show');
3051                 this.el.dom.style.display='';
3052                 Roo.get(document.body).removeClass('modal-open');
3053
3054             }
3055             this.fireEvent('hide', this);
3056         }
3057     },
3058     isVisible : function()
3059     {
3060         
3061         return this.el.hasClass('show') && !this.el.hasClass('hideing');
3062         
3063     },
3064
3065     addButton : function(str, cb)
3066     {
3067
3068
3069         var b = Roo.apply({}, { html : str } );
3070         b.xns = b.xns || Roo.bootstrap;
3071         b.xtype = b.xtype || 'Button';
3072         if (typeof(b.listeners) == 'undefined') {
3073             b.listeners = { click : cb.createDelegate(this)  };
3074         }
3075
3076         var btn = Roo.factory(b);
3077
3078         btn.render(this.getButtonContainer());
3079
3080         return btn;
3081
3082     },
3083
3084     setDefaultButton : function(btn)
3085     {
3086         //this.el.select('.modal-footer').()
3087     },
3088
3089     resizeTo: function(w,h)
3090     {
3091         this.dialogEl.setWidth(w);
3092         
3093         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
3094
3095         this.bodyEl.setHeight(h - diff);
3096         
3097         this.fireEvent('resize', this);
3098     },
3099     
3100     setContentSize  : function(w, h)
3101     {
3102
3103     },
3104     onButtonClick: function(btn,e)
3105     {
3106         //Roo.log([a,b,c]);
3107         this.fireEvent('btnclick', btn.name, e);
3108     },
3109      /**
3110      * Set the title of the Dialog
3111      * @param {String} str new Title
3112      */
3113     setTitle: function(str) {
3114         this.titleEl.dom.innerHTML = str;
3115     },
3116     /**
3117      * Set the body of the Dialog
3118      * @param {String} str new Title
3119      */
3120     setBody: function(str) {
3121         this.bodyEl.dom.innerHTML = str;
3122     },
3123     /**
3124      * Set the body of the Dialog using the template
3125      * @param {Obj} data - apply this data to the template and replace the body contents.
3126      */
3127     applyBody: function(obj)
3128     {
3129         if (!this.tmpl) {
3130             Roo.log("Error - using apply Body without a template");
3131             //code
3132         }
3133         this.tmpl.overwrite(this.bodyEl, obj);
3134     },
3135     
3136     getChildHeight : function(child_nodes)
3137     {
3138         if(
3139             !child_nodes ||
3140             child_nodes.length == 0
3141         ) {
3142             return;
3143         }
3144         
3145         var child_height = 0;
3146         
3147         for(var i = 0; i < child_nodes.length; i++) {
3148             
3149             /*
3150             * for modal with tabs...
3151             if(child_nodes[i].classList.contains('roo-layout-panel')) {
3152                 
3153                 var layout_childs = child_nodes[i].childNodes;
3154                 
3155                 for(var j = 0; j < layout_childs.length; j++) {
3156                     
3157                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
3158                         
3159                         var layout_body_childs = layout_childs[j].childNodes;
3160                         
3161                         for(var k = 0; k < layout_body_childs.length; k++) {
3162                             
3163                             if(layout_body_childs[k].classList.contains('navbar')) {
3164                                 child_height += layout_body_childs[k].offsetHeight;
3165                                 continue;
3166                             }
3167                             
3168                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
3169                                 
3170                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
3171                                 
3172                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
3173                                     
3174                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
3175                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
3176                                         continue;
3177                                     }
3178                                     
3179                                 }
3180                                 
3181                             }
3182                             
3183                         }
3184                     }
3185                 }
3186                 continue;
3187             }
3188             */
3189             
3190             child_height += child_nodes[i].offsetHeight;
3191             // Roo.log(child_nodes[i].offsetHeight);
3192         }
3193         
3194         return child_height;
3195     }
3196
3197 });
3198
3199
3200 Roo.apply(Roo.bootstrap.Modal,  {
3201     /**
3202          * Button config that displays a single OK button
3203          * @type Object
3204          */
3205         OK :  [{
3206             name : 'ok',
3207             weight : 'primary',
3208             html : 'OK'
3209         }],
3210         /**
3211          * Button config that displays Yes and No buttons
3212          * @type Object
3213          */
3214         YESNO : [
3215             {
3216                 name  : 'no',
3217                 html : 'No'
3218             },
3219             {
3220                 name  :'yes',
3221                 weight : 'primary',
3222                 html : 'Yes'
3223             }
3224         ],
3225
3226         /**
3227          * Button config that displays OK and Cancel buttons
3228          * @type Object
3229          */
3230         OKCANCEL : [
3231             {
3232                name : 'cancel',
3233                 html : 'Cancel'
3234             },
3235             {
3236                 name : 'ok',
3237                 weight : 'primary',
3238                 html : 'OK'
3239             }
3240         ],
3241         /**
3242          * Button config that displays Yes, No and Cancel buttons
3243          * @type Object
3244          */
3245         YESNOCANCEL : [
3246             {
3247                 name : 'yes',
3248                 weight : 'primary',
3249                 html : 'Yes'
3250             },
3251             {
3252                 name : 'no',
3253                 html : 'No'
3254             },
3255             {
3256                 name : 'cancel',
3257                 html : 'Cancel'
3258             }
3259         ],
3260         
3261         zIndex : 10001
3262 });
3263 /*
3264  * - LGPL
3265  *
3266  * messagebox - can be used as a replace
3267  * 
3268  */
3269 /**
3270  * @class Roo.MessageBox
3271  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
3272  * Example usage:
3273  *<pre><code>
3274 // Basic alert:
3275 Roo.Msg.alert('Status', 'Changes saved successfully.');
3276
3277 // Prompt for user data:
3278 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
3279     if (btn == 'ok'){
3280         // process text value...
3281     }
3282 });
3283
3284 // Show a dialog using config options:
3285 Roo.Msg.show({
3286    title:'Save Changes?',
3287    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3288    buttons: Roo.Msg.YESNOCANCEL,
3289    fn: processResult,
3290    animEl: 'elId'
3291 });
3292 </code></pre>
3293  * @singleton
3294  */
3295 Roo.bootstrap.MessageBox = function(){
3296     var dlg, opt, mask, waitTimer;
3297     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3298     var buttons, activeTextEl, bwidth;
3299
3300     
3301     // private
3302     var handleButton = function(button){
3303         dlg.hide();
3304         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3305     };
3306
3307     // private
3308     var handleHide = function(){
3309         if(opt && opt.cls){
3310             dlg.el.removeClass(opt.cls);
3311         }
3312         //if(waitTimer){
3313         //    Roo.TaskMgr.stop(waitTimer);
3314         //    waitTimer = null;
3315         //}
3316     };
3317
3318     // private
3319     var updateButtons = function(b){
3320         var width = 0;
3321         if(!b){
3322             buttons["ok"].hide();
3323             buttons["cancel"].hide();
3324             buttons["yes"].hide();
3325             buttons["no"].hide();
3326             dlg.footerEl.hide();
3327             
3328             return width;
3329         }
3330         dlg.footerEl.show();
3331         for(var k in buttons){
3332             if(typeof buttons[k] != "function"){
3333                 if(b[k]){
3334                     buttons[k].show();
3335                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3336                     width += buttons[k].el.getWidth()+15;
3337                 }else{
3338                     buttons[k].hide();
3339                 }
3340             }
3341         }
3342         return width;
3343     };
3344
3345     // private
3346     var handleEsc = function(d, k, e){
3347         if(opt && opt.closable !== false){
3348             dlg.hide();
3349         }
3350         if(e){
3351             e.stopEvent();
3352         }
3353     };
3354
3355     return {
3356         /**
3357          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3358          * @return {Roo.BasicDialog} The BasicDialog element
3359          */
3360         getDialog : function(){
3361            if(!dlg){
3362                 dlg = new Roo.bootstrap.Modal( {
3363                     //draggable: true,
3364                     //resizable:false,
3365                     //constraintoviewport:false,
3366                     //fixedcenter:true,
3367                     //collapsible : false,
3368                     //shim:true,
3369                     //modal: true,
3370                 //    width: 'auto',
3371                   //  height:100,
3372                     //buttonAlign:"center",
3373                     closeClick : function(){
3374                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3375                             handleButton("no");
3376                         }else{
3377                             handleButton("cancel");
3378                         }
3379                     }
3380                 });
3381                 dlg.render();
3382                 dlg.on("hide", handleHide);
3383                 mask = dlg.mask;
3384                 //dlg.addKeyListener(27, handleEsc);
3385                 buttons = {};
3386                 this.buttons = buttons;
3387                 var bt = this.buttonText;
3388                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3389                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3390                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3391                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3392                 //Roo.log(buttons);
3393                 bodyEl = dlg.bodyEl.createChild({
3394
3395                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3396                         '<textarea class="roo-mb-textarea"></textarea>' +
3397                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3398                 });
3399                 msgEl = bodyEl.dom.firstChild;
3400                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3401                 textboxEl.enableDisplayMode();
3402                 textboxEl.addKeyListener([10,13], function(){
3403                     if(dlg.isVisible() && opt && opt.buttons){
3404                         if(opt.buttons.ok){
3405                             handleButton("ok");
3406                         }else if(opt.buttons.yes){
3407                             handleButton("yes");
3408                         }
3409                     }
3410                 });
3411                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3412                 textareaEl.enableDisplayMode();
3413                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3414                 progressEl.enableDisplayMode();
3415                 
3416                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3417                 var pf = progressEl.dom.firstChild;
3418                 if (pf) {
3419                     pp = Roo.get(pf.firstChild);
3420                     pp.setHeight(pf.offsetHeight);
3421                 }
3422                 
3423             }
3424             return dlg;
3425         },
3426
3427         /**
3428          * Updates the message box body text
3429          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3430          * the XHTML-compliant non-breaking space character '&amp;#160;')
3431          * @return {Roo.MessageBox} This message box
3432          */
3433         updateText : function(text)
3434         {
3435             if(!dlg.isVisible() && !opt.width){
3436                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3437                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3438             }
3439             msgEl.innerHTML = text || '&#160;';
3440       
3441             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3442             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3443             var w = Math.max(
3444                     Math.min(opt.width || cw , this.maxWidth), 
3445                     Math.max(opt.minWidth || this.minWidth, bwidth)
3446             );
3447             if(opt.prompt){
3448                 activeTextEl.setWidth(w);
3449             }
3450             if(dlg.isVisible()){
3451                 dlg.fixedcenter = false;
3452             }
3453             // to big, make it scroll. = But as usual stupid IE does not support
3454             // !important..
3455             
3456             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3457                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3458                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3459             } else {
3460                 bodyEl.dom.style.height = '';
3461                 bodyEl.dom.style.overflowY = '';
3462             }
3463             if (cw > w) {
3464                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3465             } else {
3466                 bodyEl.dom.style.overflowX = '';
3467             }
3468             
3469             dlg.setContentSize(w, bodyEl.getHeight());
3470             if(dlg.isVisible()){
3471                 dlg.fixedcenter = true;
3472             }
3473             return this;
3474         },
3475
3476         /**
3477          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3478          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3479          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3480          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3481          * @return {Roo.MessageBox} This message box
3482          */
3483         updateProgress : function(value, text){
3484             if(text){
3485                 this.updateText(text);
3486             }
3487             
3488             if (pp) { // weird bug on my firefox - for some reason this is not defined
3489                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3490                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3491             }
3492             return this;
3493         },        
3494
3495         /**
3496          * Returns true if the message box is currently displayed
3497          * @return {Boolean} True if the message box is visible, else false
3498          */
3499         isVisible : function(){
3500             return dlg && dlg.isVisible();  
3501         },
3502
3503         /**
3504          * Hides the message box if it is displayed
3505          */
3506         hide : function(){
3507             if(this.isVisible()){
3508                 dlg.hide();
3509             }  
3510         },
3511
3512         /**
3513          * Displays a new message box, or reinitializes an existing message box, based on the config options
3514          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3515          * The following config object properties are supported:
3516          * <pre>
3517 Property    Type             Description
3518 ----------  ---------------  ------------------------------------------------------------------------------------
3519 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3520                                    closes (defaults to undefined)
3521 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3522                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3523 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3524                                    progress and wait dialogs will ignore this property and always hide the
3525                                    close button as they can only be closed programmatically.
3526 cls               String           A custom CSS class to apply to the message box element
3527 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3528                                    displayed (defaults to 75)
3529 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3530                                    function will be btn (the name of the button that was clicked, if applicable,
3531                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3532                                    Progress and wait dialogs will ignore this option since they do not respond to
3533                                    user actions and can only be closed programmatically, so any required function
3534                                    should be called by the same code after it closes the dialog.
3535 icon              String           A CSS class that provides a background image to be used as an icon for
3536                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3537 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3538 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3539 modal             Boolean          False to allow user interaction with the page while the message box is
3540                                    displayed (defaults to true)
3541 msg               String           A string that will replace the existing message box body text (defaults
3542                                    to the XHTML-compliant non-breaking space character '&#160;')
3543 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3544 progress          Boolean          True to display a progress bar (defaults to false)
3545 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3546 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3547 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3548 title             String           The title text
3549 value             String           The string value to set into the active textbox element if displayed
3550 wait              Boolean          True to display a progress bar (defaults to false)
3551 width             Number           The width of the dialog in pixels
3552 </pre>
3553          *
3554          * Example usage:
3555          * <pre><code>
3556 Roo.Msg.show({
3557    title: 'Address',
3558    msg: 'Please enter your address:',
3559    width: 300,
3560    buttons: Roo.MessageBox.OKCANCEL,
3561    multiline: true,
3562    fn: saveAddress,
3563    animEl: 'addAddressBtn'
3564 });
3565 </code></pre>
3566          * @param {Object} config Configuration options
3567          * @return {Roo.MessageBox} This message box
3568          */
3569         show : function(options)
3570         {
3571             
3572             // this causes nightmares if you show one dialog after another
3573             // especially on callbacks..
3574              
3575             if(this.isVisible()){
3576                 
3577                 this.hide();
3578                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3579                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3580                 Roo.log("New Dialog Message:" +  options.msg )
3581                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3582                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3583                 
3584             }
3585             var d = this.getDialog();
3586             opt = options;
3587             d.setTitle(opt.title || "&#160;");
3588             d.closeEl.setDisplayed(opt.closable !== false);
3589             activeTextEl = textboxEl;
3590             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3591             if(opt.prompt){
3592                 if(opt.multiline){
3593                     textboxEl.hide();
3594                     textareaEl.show();
3595                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3596                         opt.multiline : this.defaultTextHeight);
3597                     activeTextEl = textareaEl;
3598                 }else{
3599                     textboxEl.show();
3600                     textareaEl.hide();
3601                 }
3602             }else{
3603                 textboxEl.hide();
3604                 textareaEl.hide();
3605             }
3606             progressEl.setDisplayed(opt.progress === true);
3607             if (opt.progress) {
3608                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
3609             }
3610             this.updateProgress(0);
3611             activeTextEl.dom.value = opt.value || "";
3612             if(opt.prompt){
3613                 dlg.setDefaultButton(activeTextEl);
3614             }else{
3615                 var bs = opt.buttons;
3616                 var db = null;
3617                 if(bs && bs.ok){
3618                     db = buttons["ok"];
3619                 }else if(bs && bs.yes){
3620                     db = buttons["yes"];
3621                 }
3622                 dlg.setDefaultButton(db);
3623             }
3624             bwidth = updateButtons(opt.buttons);
3625             this.updateText(opt.msg);
3626             if(opt.cls){
3627                 d.el.addClass(opt.cls);
3628             }
3629             d.proxyDrag = opt.proxyDrag === true;
3630             d.modal = opt.modal !== false;
3631             d.mask = opt.modal !== false ? mask : false;
3632             if(!d.isVisible()){
3633                 // force it to the end of the z-index stack so it gets a cursor in FF
3634                 document.body.appendChild(dlg.el.dom);
3635                 d.animateTarget = null;
3636                 d.show(options.animEl);
3637             }
3638             return this;
3639         },
3640
3641         /**
3642          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3643          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3644          * and closing the message box when the process is complete.
3645          * @param {String} title The title bar text
3646          * @param {String} msg The message box body text
3647          * @return {Roo.MessageBox} This message box
3648          */
3649         progress : function(title, msg){
3650             this.show({
3651                 title : title,
3652                 msg : msg,
3653                 buttons: false,
3654                 progress:true,
3655                 closable:false,
3656                 minWidth: this.minProgressWidth,
3657                 modal : true
3658             });
3659             return this;
3660         },
3661
3662         /**
3663          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3664          * If a callback function is passed it will be called after the user clicks the button, and the
3665          * id of the button that was clicked will be passed as the only parameter to the callback
3666          * (could also be the top-right close button).
3667          * @param {String} title The title bar text
3668          * @param {String} msg The message box body text
3669          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3670          * @param {Object} scope (optional) The scope of the callback function
3671          * @return {Roo.MessageBox} This message box
3672          */
3673         alert : function(title, msg, fn, scope)
3674         {
3675             this.show({
3676                 title : title,
3677                 msg : msg,
3678                 buttons: this.OK,
3679                 fn: fn,
3680                 closable : false,
3681                 scope : scope,
3682                 modal : true
3683             });
3684             return this;
3685         },
3686
3687         /**
3688          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3689          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3690          * You are responsible for closing the message box when the process is complete.
3691          * @param {String} msg The message box body text
3692          * @param {String} title (optional) The title bar text
3693          * @return {Roo.MessageBox} This message box
3694          */
3695         wait : function(msg, title){
3696             this.show({
3697                 title : title,
3698                 msg : msg,
3699                 buttons: false,
3700                 closable:false,
3701                 progress:true,
3702                 modal:true,
3703                 width:300,
3704                 wait:true
3705             });
3706             waitTimer = Roo.TaskMgr.start({
3707                 run: function(i){
3708                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3709                 },
3710                 interval: 1000
3711             });
3712             return this;
3713         },
3714
3715         /**
3716          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3717          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3718          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3719          * @param {String} title The title bar text
3720          * @param {String} msg The message box body text
3721          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3722          * @param {Object} scope (optional) The scope of the callback function
3723          * @return {Roo.MessageBox} This message box
3724          */
3725         confirm : function(title, msg, fn, scope){
3726             this.show({
3727                 title : title,
3728                 msg : msg,
3729                 buttons: this.YESNO,
3730                 fn: fn,
3731                 scope : scope,
3732                 modal : true
3733             });
3734             return this;
3735         },
3736
3737         /**
3738          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3739          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3740          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3741          * (could also be the top-right close button) and the text that was entered will be passed as the two
3742          * parameters to the callback.
3743          * @param {String} title The title bar text
3744          * @param {String} msg The message box body text
3745          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3746          * @param {Object} scope (optional) The scope of the callback function
3747          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3748          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3749          * @return {Roo.MessageBox} This message box
3750          */
3751         prompt : function(title, msg, fn, scope, multiline){
3752             this.show({
3753                 title : title,
3754                 msg : msg,
3755                 buttons: this.OKCANCEL,
3756                 fn: fn,
3757                 minWidth:250,
3758                 scope : scope,
3759                 prompt:true,
3760                 multiline: multiline,
3761                 modal : true
3762             });
3763             return this;
3764         },
3765
3766         /**
3767          * Button config that displays a single OK button
3768          * @type Object
3769          */
3770         OK : {ok:true},
3771         /**
3772          * Button config that displays Yes and No buttons
3773          * @type Object
3774          */
3775         YESNO : {yes:true, no:true},
3776         /**
3777          * Button config that displays OK and Cancel buttons
3778          * @type Object
3779          */
3780         OKCANCEL : {ok:true, cancel:true},
3781         /**
3782          * Button config that displays Yes, No and Cancel buttons
3783          * @type Object
3784          */
3785         YESNOCANCEL : {yes:true, no:true, cancel:true},
3786
3787         /**
3788          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3789          * @type Number
3790          */
3791         defaultTextHeight : 75,
3792         /**
3793          * The maximum width in pixels of the message box (defaults to 600)
3794          * @type Number
3795          */
3796         maxWidth : 600,
3797         /**
3798          * The minimum width in pixels of the message box (defaults to 100)
3799          * @type Number
3800          */
3801         minWidth : 100,
3802         /**
3803          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3804          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3805          * @type Number
3806          */
3807         minProgressWidth : 250,
3808         /**
3809          * An object containing the default button text strings that can be overriden for localized language support.
3810          * Supported properties are: ok, cancel, yes and no.
3811          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3812          * @type Object
3813          */
3814         buttonText : {
3815             ok : "OK",
3816             cancel : "Cancel",
3817             yes : "Yes",
3818             no : "No"
3819         }
3820     };
3821 }();
3822
3823 /**
3824  * Shorthand for {@link Roo.MessageBox}
3825  */
3826 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3827 Roo.Msg = Roo.Msg || Roo.MessageBox;
3828 /*
3829  * - LGPL
3830  *
3831  * navbar
3832  * 
3833  */
3834
3835 /**
3836  * @class Roo.bootstrap.Navbar
3837  * @extends Roo.bootstrap.Component
3838  * Bootstrap Navbar class
3839
3840  * @constructor
3841  * Create a new Navbar
3842  * @param {Object} config The config object
3843  */
3844
3845
3846 Roo.bootstrap.Navbar = function(config){
3847     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3848     this.addEvents({
3849         // raw events
3850         /**
3851          * @event beforetoggle
3852          * Fire before toggle the menu
3853          * @param {Roo.EventObject} e
3854          */
3855         "beforetoggle" : true
3856     });
3857 };
3858
3859 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3860     
3861     
3862    
3863     // private
3864     navItems : false,
3865     loadMask : false,
3866     
3867     
3868     getAutoCreate : function(){
3869         
3870         
3871         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3872         
3873     },
3874     
3875     initEvents :function ()
3876     {
3877         //Roo.log(this.el.select('.navbar-toggle',true));
3878         this.el.select('.navbar-toggle',true).on('click', function() {
3879             if(this.fireEvent('beforetoggle', this) !== false){
3880                 var ce = this.el.select('.navbar-collapse',true).first();
3881                 ce.toggleClass('in'); // old...
3882                 if (ce.hasClass('collapse')) {
3883                     // show it...
3884                     ce.removeClass('collapse');
3885                     ce.addClass('show');
3886                     var h = ce.getHeight();
3887                     Roo.log(h);
3888                     ce.removeClass('show');
3889                     // at this point we should be able to see it..
3890                     ce.addClass('collapsing');
3891                     
3892                     ce.setHeight(0); // resize it ...
3893                     ce.on('transitionend', function() {
3894                         Roo.log('done transition');
3895                         ce.removeClass('collapsing');
3896                         ce.addClass('show');
3897                         ce.removeClass('collapse');
3898
3899                         ce.dom.style.height = '';
3900                     }, this, { single: true} );
3901                     ce.setHeight(h);
3902                     
3903                 } else {
3904                     ce.setHeight(ce.getHeight());
3905                     ce.removeClass('show');
3906                     ce.addClass('collapsing');
3907                     
3908                     ce.on('transitionend', function() {
3909                         ce.dom.style.height = '';
3910                         ce.removeClass('collapsing');
3911                         ce.addClass('collapse');
3912                     }, this, { single: true} );
3913                     ce.setHeight(0);
3914                 }
3915             }
3916             
3917         }, this);
3918         
3919         var mark = {
3920             tag: "div",
3921             cls:"x-dlg-mask"
3922         };
3923         
3924         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3925         
3926         var size = this.el.getSize();
3927         this.maskEl.setSize(size.width, size.height);
3928         this.maskEl.enableDisplayMode("block");
3929         this.maskEl.hide();
3930         
3931         if(this.loadMask){
3932             this.maskEl.show();
3933         }
3934     },
3935     
3936     
3937     getChildContainer : function()
3938     {
3939         if (this.el && this.el.select('.collapse').getCount()) {
3940             return this.el.select('.collapse',true).first();
3941         }
3942         
3943         return this.el;
3944     },
3945     
3946     mask : function()
3947     {
3948         this.maskEl.show();
3949     },
3950     
3951     unmask : function()
3952     {
3953         this.maskEl.hide();
3954     } 
3955     
3956     
3957     
3958     
3959 });
3960
3961
3962
3963  
3964
3965  /*
3966  * - LGPL
3967  *
3968  * navbar
3969  * 
3970  */
3971
3972 /**
3973  * @class Roo.bootstrap.NavSimplebar
3974  * @extends Roo.bootstrap.Navbar
3975  * Bootstrap Sidebar class
3976  *
3977  * @cfg {Boolean} inverse is inverted color
3978  * 
3979  * @cfg {String} type (nav | pills | tabs)
3980  * @cfg {Boolean} arrangement stacked | justified
3981  * @cfg {String} align (left | right) alignment
3982  * 
3983  * @cfg {Boolean} main (true|false) main nav bar? default false
3984  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3985  * 
3986  * @cfg {String} tag (header|footer|nav|div) default is nav 
3987
3988  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
3989  * 
3990  * 
3991  * @constructor
3992  * Create a new Sidebar
3993  * @param {Object} config The config object
3994  */
3995
3996
3997 Roo.bootstrap.NavSimplebar = function(config){
3998     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3999 };
4000
4001 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
4002     
4003     inverse: false,
4004     
4005     type: false,
4006     arrangement: '',
4007     align : false,
4008     
4009     weight : 'light',
4010     
4011     main : false,
4012     
4013     
4014     tag : false,
4015     
4016     
4017     getAutoCreate : function(){
4018         
4019         
4020         var cfg = {
4021             tag : this.tag || 'div',
4022             cls : 'navbar navbar-expand-lg roo-navbar-simple'
4023         };
4024         if (['light','white'].indexOf(this.weight) > -1) {
4025             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4026         }
4027         cfg.cls += ' bg-' + this.weight;
4028         
4029         if (this.inverse) {
4030             cfg.cls += ' navbar-inverse';
4031             
4032         }
4033         
4034         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
4035         
4036         //if (Roo.bootstrap.version == 4) {
4037         //    return cfg;
4038         //}
4039         
4040         cfg.cn = [
4041             {
4042                 cls: 'nav',
4043                 tag : 'ul'
4044             }
4045         ];
4046         
4047          
4048         this.type = this.type || 'nav';
4049         if (['tabs','pills'].indexOf(this.type) != -1) {
4050             cfg.cn[0].cls += ' nav-' + this.type
4051         
4052         
4053         } else {
4054             if (this.type!=='nav') {
4055                 Roo.log('nav type must be nav/tabs/pills')
4056             }
4057             cfg.cn[0].cls += ' navbar-nav'
4058         }
4059         
4060         
4061         
4062         
4063         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
4064             cfg.cn[0].cls += ' nav-' + this.arrangement;
4065         }
4066         
4067         
4068         if (this.align === 'right') {
4069             cfg.cn[0].cls += ' navbar-right';
4070         }
4071         
4072         
4073         
4074         
4075         return cfg;
4076     
4077         
4078     }
4079     
4080     
4081     
4082 });
4083
4084
4085
4086  
4087
4088  
4089        /*
4090  * - LGPL
4091  *
4092  * navbar
4093  * navbar-fixed-top
4094  * navbar-expand-md  fixed-top 
4095  */
4096
4097 /**
4098  * @class Roo.bootstrap.NavHeaderbar
4099  * @extends Roo.bootstrap.NavSimplebar
4100  * Bootstrap Sidebar class
4101  *
4102  * @cfg {String} brand what is brand
4103  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
4104  * @cfg {String} brand_href href of the brand
4105  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
4106  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
4107  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
4108  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
4109  * 
4110  * @constructor
4111  * Create a new Sidebar
4112  * @param {Object} config The config object
4113  */
4114
4115
4116 Roo.bootstrap.NavHeaderbar = function(config){
4117     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
4118       
4119 };
4120
4121 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
4122     
4123     position: '',
4124     brand: '',
4125     brand_href: false,
4126     srButton : true,
4127     autohide : false,
4128     desktopCenter : false,
4129    
4130     
4131     getAutoCreate : function(){
4132         
4133         var   cfg = {
4134             tag: this.nav || 'nav',
4135             cls: 'navbar navbar-expand-md',
4136             role: 'navigation',
4137             cn: []
4138         };
4139         
4140         var cn = cfg.cn;
4141         if (this.desktopCenter) {
4142             cn.push({cls : 'container', cn : []});
4143             cn = cn[0].cn;
4144         }
4145         
4146         if(this.srButton){
4147             var btn = {
4148                 tag: 'button',
4149                 type: 'button',
4150                 cls: 'navbar-toggle navbar-toggler',
4151                 'data-toggle': 'collapse',
4152                 cn: [
4153                     {
4154                         tag: 'span',
4155                         cls: 'sr-only',
4156                         html: 'Toggle navigation'
4157                     },
4158                     {
4159                         tag: 'span',
4160                         cls: 'icon-bar navbar-toggler-icon'
4161                     },
4162                     {
4163                         tag: 'span',
4164                         cls: 'icon-bar'
4165                     },
4166                     {
4167                         tag: 'span',
4168                         cls: 'icon-bar'
4169                     }
4170                 ]
4171             };
4172             
4173             cn.push( Roo.bootstrap.version == 4 ? btn : {
4174                 tag: 'div',
4175                 cls: 'navbar-header',
4176                 cn: [
4177                     btn
4178                 ]
4179             });
4180         }
4181         
4182         cn.push({
4183             tag: 'div',
4184             cls: 'collapse navbar-collapse',
4185             cn : []
4186         });
4187         
4188         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
4189         
4190         if (['light','white'].indexOf(this.weight) > -1) {
4191             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
4192         }
4193         cfg.cls += ' bg-' + this.weight;
4194         
4195         
4196         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
4197             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
4198             
4199             // tag can override this..
4200             
4201             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
4202         }
4203         
4204         if (this.brand !== '') {
4205             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
4206             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
4207                 tag: 'a',
4208                 href: this.brand_href ? this.brand_href : '#',
4209                 cls: 'navbar-brand',
4210                 cn: [
4211                 this.brand
4212                 ]
4213             });
4214         }
4215         
4216         if(this.main){
4217             cfg.cls += ' main-nav';
4218         }
4219         
4220         
4221         return cfg;
4222
4223         
4224     },
4225     getHeaderChildContainer : function()
4226     {
4227         if (this.srButton && this.el.select('.navbar-header').getCount()) {
4228             return this.el.select('.navbar-header',true).first();
4229         }
4230         
4231         return this.getChildContainer();
4232     },
4233     
4234     
4235     initEvents : function()
4236     {
4237         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
4238         
4239         if (this.autohide) {
4240             
4241             var prevScroll = 0;
4242             var ft = this.el;
4243             
4244             Roo.get(document).on('scroll',function(e) {
4245                 var ns = Roo.get(document).getScroll().top;
4246                 var os = prevScroll;
4247                 prevScroll = ns;
4248                 
4249                 if(ns > os){
4250                     ft.removeClass('slideDown');
4251                     ft.addClass('slideUp');
4252                     return;
4253                 }
4254                 ft.removeClass('slideUp');
4255                 ft.addClass('slideDown');
4256                  
4257               
4258           },this);
4259         }
4260     }    
4261     
4262 });
4263
4264
4265
4266  
4267
4268  /*
4269  * - LGPL
4270  *
4271  * navbar
4272  * 
4273  */
4274
4275 /**
4276  * @class Roo.bootstrap.NavSidebar
4277  * @extends Roo.bootstrap.Navbar
4278  * Bootstrap Sidebar class
4279  * 
4280  * @constructor
4281  * Create a new Sidebar
4282  * @param {Object} config The config object
4283  */
4284
4285
4286 Roo.bootstrap.NavSidebar = function(config){
4287     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
4288 };
4289
4290 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
4291     
4292     sidebar : true, // used by Navbar Item and NavbarGroup at present...
4293     
4294     getAutoCreate : function(){
4295         
4296         
4297         return  {
4298             tag: 'div',
4299             cls: 'sidebar sidebar-nav'
4300         };
4301     
4302         
4303     }
4304     
4305     
4306     
4307 });
4308
4309
4310
4311  
4312
4313  /*
4314  * - LGPL
4315  *
4316  * nav group
4317  * 
4318  */
4319
4320 /**
4321  * @class Roo.bootstrap.NavGroup
4322  * @extends Roo.bootstrap.Component
4323  * Bootstrap NavGroup class
4324  * @cfg {String} align (left|right)
4325  * @cfg {Boolean} inverse
4326  * @cfg {String} type (nav|pills|tab) default nav
4327  * @cfg {String} navId - reference Id for navbar.
4328
4329  * 
4330  * @constructor
4331  * Create a new nav group
4332  * @param {Object} config The config object
4333  */
4334
4335 Roo.bootstrap.NavGroup = function(config){
4336     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
4337     this.navItems = [];
4338    
4339     Roo.bootstrap.NavGroup.register(this);
4340      this.addEvents({
4341         /**
4342              * @event changed
4343              * Fires when the active item changes
4344              * @param {Roo.bootstrap.NavGroup} this
4345              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4346              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4347          */
4348         'changed': true
4349      });
4350     
4351 };
4352
4353 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4354     
4355     align: '',
4356     inverse: false,
4357     form: false,
4358     type: 'nav',
4359     navId : '',
4360     // private
4361     
4362     navItems : false, 
4363     
4364     getAutoCreate : function()
4365     {
4366         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4367         
4368         cfg = {
4369             tag : 'ul',
4370             cls: 'nav' 
4371         };
4372         if (Roo.bootstrap.version == 4) {
4373             if (['tabs','pills'].indexOf(this.type) != -1) {
4374                 cfg.cls += ' nav-' + this.type; 
4375             } else {
4376                 cfg.cls += ' navbar-nav';
4377             }
4378         } else {
4379             if (['tabs','pills'].indexOf(this.type) != -1) {
4380                 cfg.cls += ' nav-' + this.type
4381             } else {
4382                 if (this.type !== 'nav') {
4383                     Roo.log('nav type must be nav/tabs/pills')
4384                 }
4385                 cfg.cls += ' navbar-nav'
4386             }
4387         }
4388         
4389         if (this.parent() && this.parent().sidebar) {
4390             cfg = {
4391                 tag: 'ul',
4392                 cls: 'dashboard-menu sidebar-menu'
4393             };
4394             
4395             return cfg;
4396         }
4397         
4398         if (this.form === true) {
4399             cfg = {
4400                 tag: 'form',
4401                 cls: 'navbar-form form-inline'
4402             };
4403             
4404             if (this.align === 'right') {
4405                 cfg.cls += ' navbar-right ml-md-auto';
4406             } else {
4407                 cfg.cls += ' navbar-left';
4408             }
4409         }
4410         
4411         if (this.align === 'right') {
4412             cfg.cls += ' navbar-right ml-md-auto';
4413         } else {
4414             cfg.cls += ' mr-auto';
4415         }
4416         
4417         if (this.inverse) {
4418             cfg.cls += ' navbar-inverse';
4419             
4420         }
4421         
4422         
4423         return cfg;
4424     },
4425     /**
4426     * sets the active Navigation item
4427     * @param {Roo.bootstrap.NavItem} the new current navitem
4428     */
4429     setActiveItem : function(item)
4430     {
4431         var prev = false;
4432         Roo.each(this.navItems, function(v){
4433             if (v == item) {
4434                 return ;
4435             }
4436             if (v.isActive()) {
4437                 v.setActive(false, true);
4438                 prev = v;
4439                 
4440             }
4441             
4442         });
4443
4444         item.setActive(true, true);
4445         this.fireEvent('changed', this, item, prev);
4446         
4447         
4448     },
4449     /**
4450     * gets the active Navigation item
4451     * @return {Roo.bootstrap.NavItem} the current navitem
4452     */
4453     getActive : function()
4454     {
4455         
4456         var prev = false;
4457         Roo.each(this.navItems, function(v){
4458             
4459             if (v.isActive()) {
4460                 prev = v;
4461                 
4462             }
4463             
4464         });
4465         return prev;
4466     },
4467     
4468     indexOfNav : function()
4469     {
4470         
4471         var prev = false;
4472         Roo.each(this.navItems, function(v,i){
4473             
4474             if (v.isActive()) {
4475                 prev = i;
4476                 
4477             }
4478             
4479         });
4480         return prev;
4481     },
4482     /**
4483     * adds a Navigation item
4484     * @param {Roo.bootstrap.NavItem} the navitem to add
4485     */
4486     addItem : function(cfg)
4487     {
4488         if (this.form && Roo.bootstrap.version == 4) {
4489             cfg.tag = 'div';
4490         }
4491         var cn = new Roo.bootstrap.NavItem(cfg);
4492         this.register(cn);
4493         cn.parentId = this.id;
4494         cn.onRender(this.el, null);
4495         return cn;
4496     },
4497     /**
4498     * register a Navigation item
4499     * @param {Roo.bootstrap.NavItem} the navitem to add
4500     */
4501     register : function(item)
4502     {
4503         this.navItems.push( item);
4504         item.navId = this.navId;
4505     
4506     },
4507     
4508     /**
4509     * clear all the Navigation item
4510     */
4511    
4512     clearAll : function()
4513     {
4514         this.navItems = [];
4515         this.el.dom.innerHTML = '';
4516     },
4517     
4518     getNavItem: function(tabId)
4519     {
4520         var ret = false;
4521         Roo.each(this.navItems, function(e) {
4522             if (e.tabId == tabId) {
4523                ret =  e;
4524                return false;
4525             }
4526             return true;
4527             
4528         });
4529         return ret;
4530     },
4531     
4532     setActiveNext : function()
4533     {
4534         var i = this.indexOfNav(this.getActive());
4535         if (i > this.navItems.length) {
4536             return;
4537         }
4538         this.setActiveItem(this.navItems[i+1]);
4539     },
4540     setActivePrev : function()
4541     {
4542         var i = this.indexOfNav(this.getActive());
4543         if (i  < 1) {
4544             return;
4545         }
4546         this.setActiveItem(this.navItems[i-1]);
4547     },
4548     clearWasActive : function(except) {
4549         Roo.each(this.navItems, function(e) {
4550             if (e.tabId != except.tabId && e.was_active) {
4551                e.was_active = false;
4552                return false;
4553             }
4554             return true;
4555             
4556         });
4557     },
4558     getWasActive : function ()
4559     {
4560         var r = false;
4561         Roo.each(this.navItems, function(e) {
4562             if (e.was_active) {
4563                r = e;
4564                return false;
4565             }
4566             return true;
4567             
4568         });
4569         return r;
4570     }
4571     
4572     
4573 });
4574
4575  
4576 Roo.apply(Roo.bootstrap.NavGroup, {
4577     
4578     groups: {},
4579      /**
4580     * register a Navigation Group
4581     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4582     */
4583     register : function(navgrp)
4584     {
4585         this.groups[navgrp.navId] = navgrp;
4586         
4587     },
4588     /**
4589     * fetch a Navigation Group based on the navigation ID
4590     * @param {string} the navgroup to add
4591     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4592     */
4593     get: function(navId) {
4594         if (typeof(this.groups[navId]) == 'undefined') {
4595             return false;
4596             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4597         }
4598         return this.groups[navId] ;
4599     }
4600     
4601     
4602     
4603 });
4604
4605  /*
4606  * - LGPL
4607  *
4608  * row
4609  * 
4610  */
4611
4612 /**
4613  * @class Roo.bootstrap.NavItem
4614  * @extends Roo.bootstrap.Component
4615  * Bootstrap Navbar.NavItem class
4616  * @cfg {String} href  link to
4617  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
4618
4619  * @cfg {String} html content of button
4620  * @cfg {String} badge text inside badge
4621  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4622  * @cfg {String} glyphicon DEPRICATED - use fa
4623  * @cfg {String} icon DEPRICATED - use fa
4624  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
4625  * @cfg {Boolean} active Is item active
4626  * @cfg {Boolean} disabled Is item disabled
4627  
4628  * @cfg {Boolean} preventDefault (true | false) default false
4629  * @cfg {String} tabId the tab that this item activates.
4630  * @cfg {String} tagtype (a|span) render as a href or span?
4631  * @cfg {Boolean} animateRef (true|false) link to element default false  
4632   
4633  * @constructor
4634  * Create a new Navbar Item
4635  * @param {Object} config The config object
4636  */
4637 Roo.bootstrap.NavItem = function(config){
4638     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4639     this.addEvents({
4640         // raw events
4641         /**
4642          * @event click
4643          * The raw click event for the entire grid.
4644          * @param {Roo.EventObject} e
4645          */
4646         "click" : true,
4647          /**
4648             * @event changed
4649             * Fires when the active item active state changes
4650             * @param {Roo.bootstrap.NavItem} this
4651             * @param {boolean} state the new state
4652              
4653          */
4654         'changed': true,
4655         /**
4656             * @event scrollto
4657             * Fires when scroll to element
4658             * @param {Roo.bootstrap.NavItem} this
4659             * @param {Object} options
4660             * @param {Roo.EventObject} e
4661              
4662          */
4663         'scrollto': true
4664     });
4665    
4666 };
4667
4668 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4669     
4670     href: false,
4671     html: '',
4672     badge: '',
4673     icon: false,
4674     fa : false,
4675     glyphicon: false,
4676     active: false,
4677     preventDefault : false,
4678     tabId : false,
4679     tagtype : 'a',
4680     tag: 'li',
4681     disabled : false,
4682     animateRef : false,
4683     was_active : false,
4684     button_weight : '',
4685     button_outline : false,
4686     
4687     navLink: false,
4688     
4689     getAutoCreate : function(){
4690          
4691         var cfg = {
4692             tag: this.tag,
4693             cls: 'nav-item'
4694         };
4695         
4696         if (this.active) {
4697             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4698         }
4699         if (this.disabled) {
4700             cfg.cls += ' disabled';
4701         }
4702         
4703         // BS4 only?
4704         if (this.button_weight.length) {
4705             cfg.tag = this.href ? 'a' : 'button';
4706             cfg.html = this.html || '';
4707             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
4708             if (this.href) {
4709                 cfg.href = this.href;
4710             }
4711             if (this.fa) {
4712                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
4713             }
4714             
4715             // menu .. should add dropdown-menu class - so no need for carat..
4716             
4717             if (this.badge !== '') {
4718                  
4719                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4720             }
4721             return cfg;
4722         }
4723         
4724         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
4725             cfg.cn = [
4726                 {
4727                     tag: this.tagtype,
4728                     href : this.href || "#",
4729                     html: this.html || ''
4730                 }
4731             ];
4732             if (this.tagtype == 'a') {
4733                 cfg.cn[0].cls = 'nav-link';
4734             }
4735             if (this.icon) {
4736                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
4737             }
4738             if (this.fa) {
4739                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
4740             }
4741             if(this.glyphicon) {
4742                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4743             }
4744             
4745             if (this.menu) {
4746                 
4747                 cfg.cn[0].html += " <span class='caret'></span>";
4748              
4749             }
4750             
4751             if (this.badge !== '') {
4752                  
4753                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
4754             }
4755         }
4756         
4757         
4758         
4759         return cfg;
4760     },
4761     onRender : function(ct, position)
4762     {
4763        // Roo.log("Call onRender: " + this.xtype);
4764         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
4765             this.tag = 'div';
4766         }
4767         
4768         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
4769         this.navLink = this.el.select('.nav-link',true).first();
4770         return ret;
4771     },
4772       
4773     
4774     initEvents: function() 
4775     {
4776         if (typeof (this.menu) != 'undefined') {
4777             this.menu.parentType = this.xtype;
4778             this.menu.triggerEl = this.el;
4779             this.menu = this.addxtype(Roo.apply({}, this.menu));
4780         }
4781         
4782         this.el.select('a',true).on('click', this.onClick, this);
4783         
4784         if(this.tagtype == 'span'){
4785             this.el.select('span',true).on('click', this.onClick, this);
4786         }
4787        
4788         // at this point parent should be available..
4789         this.parent().register(this);
4790     },
4791     
4792     onClick : function(e)
4793     {
4794         if (e.getTarget('.dropdown-menu-item')) {
4795             // did you click on a menu itemm.... - then don't trigger onclick..
4796             return;
4797         }
4798         
4799         if(
4800                 this.preventDefault || 
4801                 this.href == '#' 
4802         ){
4803             Roo.log("NavItem - prevent Default?");
4804             e.preventDefault();
4805         }
4806         
4807         if (this.disabled) {
4808             return;
4809         }
4810         
4811         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4812         if (tg && tg.transition) {
4813             Roo.log("waiting for the transitionend");
4814             return;
4815         }
4816         
4817         
4818         
4819         //Roo.log("fire event clicked");
4820         if(this.fireEvent('click', this, e) === false){
4821             return;
4822         };
4823         
4824         if(this.tagtype == 'span'){
4825             return;
4826         }
4827         
4828         //Roo.log(this.href);
4829         var ael = this.el.select('a',true).first();
4830         //Roo.log(ael);
4831         
4832         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4833             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4834             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4835                 return; // ignore... - it's a 'hash' to another page.
4836             }
4837             Roo.log("NavItem - prevent Default?");
4838             e.preventDefault();
4839             this.scrollToElement(e);
4840         }
4841         
4842         
4843         var p =  this.parent();
4844    
4845         if (['tabs','pills'].indexOf(p.type)!==-1) {
4846             if (typeof(p.setActiveItem) !== 'undefined') {
4847                 p.setActiveItem(this);
4848             }
4849         }
4850         
4851         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4852         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4853             // remove the collapsed menu expand...
4854             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4855         }
4856     },
4857     
4858     isActive: function () {
4859         return this.active
4860     },
4861     setActive : function(state, fire, is_was_active)
4862     {
4863         if (this.active && !state && this.navId) {
4864             this.was_active = true;
4865             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4866             if (nv) {
4867                 nv.clearWasActive(this);
4868             }
4869             
4870         }
4871         this.active = state;
4872         
4873         if (!state ) {
4874             this.el.removeClass('active');
4875             this.navLink ? this.navLink.removeClass('active') : false;
4876         } else if (!this.el.hasClass('active')) {
4877             
4878             this.el.addClass('active');
4879             if (Roo.bootstrap.version == 4 && this.navLink ) {
4880                 this.navLink.addClass('active');
4881             }
4882             
4883         }
4884         if (fire) {
4885             this.fireEvent('changed', this, state);
4886         }
4887         
4888         // show a panel if it's registered and related..
4889         
4890         if (!this.navId || !this.tabId || !state || is_was_active) {
4891             return;
4892         }
4893         
4894         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4895         if (!tg) {
4896             return;
4897         }
4898         var pan = tg.getPanelByName(this.tabId);
4899         if (!pan) {
4900             return;
4901         }
4902         // if we can not flip to new panel - go back to old nav highlight..
4903         if (false == tg.showPanel(pan)) {
4904             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4905             if (nv) {
4906                 var onav = nv.getWasActive();
4907                 if (onav) {
4908                     onav.setActive(true, false, true);
4909                 }
4910             }
4911             
4912         }
4913         
4914         
4915         
4916     },
4917      // this should not be here...
4918     setDisabled : function(state)
4919     {
4920         this.disabled = state;
4921         if (!state ) {
4922             this.el.removeClass('disabled');
4923         } else if (!this.el.hasClass('disabled')) {
4924             this.el.addClass('disabled');
4925         }
4926         
4927     },
4928     
4929     /**
4930      * Fetch the element to display the tooltip on.
4931      * @return {Roo.Element} defaults to this.el
4932      */
4933     tooltipEl : function()
4934     {
4935         return this.el.select('' + this.tagtype + '', true).first();
4936     },
4937     
4938     scrollToElement : function(e)
4939     {
4940         var c = document.body;
4941         
4942         /*
4943          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4944          */
4945         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4946             c = document.documentElement;
4947         }
4948         
4949         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4950         
4951         if(!target){
4952             return;
4953         }
4954
4955         var o = target.calcOffsetsTo(c);
4956         
4957         var options = {
4958             target : target,
4959             value : o[1]
4960         };
4961         
4962         this.fireEvent('scrollto', this, options, e);
4963         
4964         Roo.get(c).scrollTo('top', options.value, true);
4965         
4966         return;
4967     }
4968 });
4969  
4970
4971  /*
4972  * - LGPL
4973  *
4974  * sidebar item
4975  *
4976  *  li
4977  *    <span> icon </span>
4978  *    <span> text </span>
4979  *    <span>badge </span>
4980  */
4981
4982 /**
4983  * @class Roo.bootstrap.NavSidebarItem
4984  * @extends Roo.bootstrap.NavItem
4985  * Bootstrap Navbar.NavSidebarItem class
4986  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4987  * {Boolean} open is the menu open
4988  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4989  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4990  * {String} buttonSize (sm|md|lg)the extra classes for the button
4991  * {Boolean} showArrow show arrow next to the text (default true)
4992  * @constructor
4993  * Create a new Navbar Button
4994  * @param {Object} config The config object
4995  */
4996 Roo.bootstrap.NavSidebarItem = function(config){
4997     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4998     this.addEvents({
4999         // raw events
5000         /**
5001          * @event click
5002          * The raw click event for the entire grid.
5003          * @param {Roo.EventObject} e
5004          */
5005         "click" : true,
5006          /**
5007             * @event changed
5008             * Fires when the active item active state changes
5009             * @param {Roo.bootstrap.NavSidebarItem} this
5010             * @param {boolean} state the new state
5011              
5012          */
5013         'changed': true
5014     });
5015    
5016 };
5017
5018 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
5019     
5020     badgeWeight : 'default',
5021     
5022     open: false,
5023     
5024     buttonView : false,
5025     
5026     buttonWeight : 'default',
5027     
5028     buttonSize : 'md',
5029     
5030     showArrow : true,
5031     
5032     getAutoCreate : function(){
5033         
5034         
5035         var a = {
5036                 tag: 'a',
5037                 href : this.href || '#',
5038                 cls: '',
5039                 html : '',
5040                 cn : []
5041         };
5042         
5043         if(this.buttonView){
5044             a = {
5045                 tag: 'button',
5046                 href : this.href || '#',
5047                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
5048                 html : this.html,
5049                 cn : []
5050             };
5051         }
5052         
5053         var cfg = {
5054             tag: 'li',
5055             cls: '',
5056             cn: [ a ]
5057         };
5058         
5059         if (this.active) {
5060             cfg.cls += ' active';
5061         }
5062         
5063         if (this.disabled) {
5064             cfg.cls += ' disabled';
5065         }
5066         if (this.open) {
5067             cfg.cls += ' open x-open';
5068         }
5069         // left icon..
5070         if (this.glyphicon || this.icon) {
5071             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
5072             a.cn.push({ tag : 'i', cls : c }) ;
5073         }
5074         
5075         if(!this.buttonView){
5076             var span = {
5077                 tag: 'span',
5078                 html : this.html || ''
5079             };
5080
5081             a.cn.push(span);
5082             
5083         }
5084         
5085         if (this.badge !== '') {
5086             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
5087         }
5088         
5089         if (this.menu) {
5090             
5091             if(this.showArrow){
5092                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
5093             }
5094             
5095             a.cls += ' dropdown-toggle treeview' ;
5096         }
5097         
5098         return cfg;
5099     },
5100     
5101     initEvents : function()
5102     { 
5103         if (typeof (this.menu) != 'undefined') {
5104             this.menu.parentType = this.xtype;
5105             this.menu.triggerEl = this.el;
5106             this.menu = this.addxtype(Roo.apply({}, this.menu));
5107         }
5108         
5109         this.el.on('click', this.onClick, this);
5110         
5111         if(this.badge !== ''){
5112             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
5113         }
5114         
5115     },
5116     
5117     onClick : function(e)
5118     {
5119         if(this.disabled){
5120             e.preventDefault();
5121             return;
5122         }
5123         
5124         if(this.preventDefault){
5125             e.preventDefault();
5126         }
5127         
5128         this.fireEvent('click', this, e);
5129     },
5130     
5131     disable : function()
5132     {
5133         this.setDisabled(true);
5134     },
5135     
5136     enable : function()
5137     {
5138         this.setDisabled(false);
5139     },
5140     
5141     setDisabled : function(state)
5142     {
5143         if(this.disabled == state){
5144             return;
5145         }
5146         
5147         this.disabled = state;
5148         
5149         if (state) {
5150             this.el.addClass('disabled');
5151             return;
5152         }
5153         
5154         this.el.removeClass('disabled');
5155         
5156         return;
5157     },
5158     
5159     setActive : function(state)
5160     {
5161         if(this.active == state){
5162             return;
5163         }
5164         
5165         this.active = state;
5166         
5167         if (state) {
5168             this.el.addClass('active');
5169             return;
5170         }
5171         
5172         this.el.removeClass('active');
5173         
5174         return;
5175     },
5176     
5177     isActive: function () 
5178     {
5179         return this.active;
5180     },
5181     
5182     setBadge : function(str)
5183     {
5184         if(!this.badgeEl){
5185             return;
5186         }
5187         
5188         this.badgeEl.dom.innerHTML = str;
5189     }
5190     
5191    
5192      
5193  
5194 });
5195  
5196
5197  /*
5198  * - LGPL
5199  *
5200  * row
5201  * 
5202  */
5203
5204 /**
5205  * @class Roo.bootstrap.Row
5206  * @extends Roo.bootstrap.Component
5207  * Bootstrap Row class (contains columns...)
5208  * 
5209  * @constructor
5210  * Create a new Row
5211  * @param {Object} config The config object
5212  */
5213
5214 Roo.bootstrap.Row = function(config){
5215     Roo.bootstrap.Row.superclass.constructor.call(this, config);
5216 };
5217
5218 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
5219     
5220     getAutoCreate : function(){
5221        return {
5222             cls: 'row clearfix'
5223        };
5224     }
5225     
5226     
5227 });
5228
5229  
5230
5231  /*
5232  * - LGPL
5233  *
5234  * element
5235  * 
5236  */
5237
5238 /**
5239  * @class Roo.bootstrap.Element
5240  * @extends Roo.bootstrap.Component
5241  * Bootstrap Element class
5242  * @cfg {String} html contents of the element
5243  * @cfg {String} tag tag of the element
5244  * @cfg {String} cls class of the element
5245  * @cfg {Boolean} preventDefault (true|false) default false
5246  * @cfg {Boolean} clickable (true|false) default false
5247  * 
5248  * @constructor
5249  * Create a new Element
5250  * @param {Object} config The config object
5251  */
5252
5253 Roo.bootstrap.Element = function(config){
5254     Roo.bootstrap.Element.superclass.constructor.call(this, config);
5255     
5256     this.addEvents({
5257         // raw events
5258         /**
5259          * @event click
5260          * When a element is chick
5261          * @param {Roo.bootstrap.Element} this
5262          * @param {Roo.EventObject} e
5263          */
5264         "click" : true
5265     });
5266 };
5267
5268 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
5269     
5270     tag: 'div',
5271     cls: '',
5272     html: '',
5273     preventDefault: false, 
5274     clickable: false,
5275     
5276     getAutoCreate : function(){
5277         
5278         var cfg = {
5279             tag: this.tag,
5280             // cls: this.cls, double assign in parent class Component.js :: onRender
5281             html: this.html
5282         };
5283         
5284         return cfg;
5285     },
5286     
5287     initEvents: function() 
5288     {
5289         Roo.bootstrap.Element.superclass.initEvents.call(this);
5290         
5291         if(this.clickable){
5292             this.el.on('click', this.onClick, this);
5293         }
5294         
5295     },
5296     
5297     onClick : function(e)
5298     {
5299         if(this.preventDefault){
5300             e.preventDefault();
5301         }
5302         
5303         this.fireEvent('click', this, e);
5304     },
5305     
5306     getValue : function()
5307     {
5308         return this.el.dom.innerHTML;
5309     },
5310     
5311     setValue : function(value)
5312     {
5313         this.el.dom.innerHTML = value;
5314     }
5315    
5316 });
5317
5318  
5319
5320  /*
5321  * - LGPL
5322  *
5323  * pagination
5324  * 
5325  */
5326
5327 /**
5328  * @class Roo.bootstrap.Pagination
5329  * @extends Roo.bootstrap.Component
5330  * Bootstrap Pagination class
5331  * @cfg {String} size xs | sm | md | lg
5332  * @cfg {Boolean} inverse false | true
5333  * 
5334  * @constructor
5335  * Create a new Pagination
5336  * @param {Object} config The config object
5337  */
5338
5339 Roo.bootstrap.Pagination = function(config){
5340     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
5341 };
5342
5343 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
5344     
5345     cls: false,
5346     size: false,
5347     inverse: false,
5348     
5349     getAutoCreate : function(){
5350         var cfg = {
5351             tag: 'ul',
5352                 cls: 'pagination'
5353         };
5354         if (this.inverse) {
5355             cfg.cls += ' inverse';
5356         }
5357         if (this.html) {
5358             cfg.html=this.html;
5359         }
5360         if (this.cls) {
5361             cfg.cls += " " + this.cls;
5362         }
5363         return cfg;
5364     }
5365    
5366 });
5367
5368  
5369
5370  /*
5371  * - LGPL
5372  *
5373  * Pagination item
5374  * 
5375  */
5376
5377
5378 /**
5379  * @class Roo.bootstrap.PaginationItem
5380  * @extends Roo.bootstrap.Component
5381  * Bootstrap PaginationItem class
5382  * @cfg {String} html text
5383  * @cfg {String} href the link
5384  * @cfg {Boolean} preventDefault (true | false) default true
5385  * @cfg {Boolean} active (true | false) default false
5386  * @cfg {Boolean} disabled default false
5387  * 
5388  * 
5389  * @constructor
5390  * Create a new PaginationItem
5391  * @param {Object} config The config object
5392  */
5393
5394
5395 Roo.bootstrap.PaginationItem = function(config){
5396     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
5397     this.addEvents({
5398         // raw events
5399         /**
5400          * @event click
5401          * The raw click event for the entire grid.
5402          * @param {Roo.EventObject} e
5403          */
5404         "click" : true
5405     });
5406 };
5407
5408 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5409     
5410     href : false,
5411     html : false,
5412     preventDefault: true,
5413     active : false,
5414     cls : false,
5415     disabled: false,
5416     
5417     getAutoCreate : function(){
5418         var cfg= {
5419             tag: 'li',
5420             cn: [
5421                 {
5422                     tag : 'a',
5423                     href : this.href ? this.href : '#',
5424                     html : this.html ? this.html : ''
5425                 }
5426             ]
5427         };
5428         
5429         if(this.cls){
5430             cfg.cls = this.cls;
5431         }
5432         
5433         if(this.disabled){
5434             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5435         }
5436         
5437         if(this.active){
5438             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5439         }
5440         
5441         return cfg;
5442     },
5443     
5444     initEvents: function() {
5445         
5446         this.el.on('click', this.onClick, this);
5447         
5448     },
5449     onClick : function(e)
5450     {
5451         Roo.log('PaginationItem on click ');
5452         if(this.preventDefault){
5453             e.preventDefault();
5454         }
5455         
5456         if(this.disabled){
5457             return;
5458         }
5459         
5460         this.fireEvent('click', this, e);
5461     }
5462    
5463 });
5464
5465  
5466
5467  /*
5468  * - LGPL
5469  *
5470  * slider
5471  * 
5472  */
5473
5474
5475 /**
5476  * @class Roo.bootstrap.Slider
5477  * @extends Roo.bootstrap.Component
5478  * Bootstrap Slider class
5479  *    
5480  * @constructor
5481  * Create a new Slider
5482  * @param {Object} config The config object
5483  */
5484
5485 Roo.bootstrap.Slider = function(config){
5486     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5487 };
5488
5489 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5490     
5491     getAutoCreate : function(){
5492         
5493         var cfg = {
5494             tag: 'div',
5495             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5496             cn: [
5497                 {
5498                     tag: 'a',
5499                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5500                 }
5501             ]
5502         };
5503         
5504         return cfg;
5505     }
5506    
5507 });
5508
5509  /*
5510  * Based on:
5511  * Ext JS Library 1.1.1
5512  * Copyright(c) 2006-2007, Ext JS, LLC.
5513  *
5514  * Originally Released Under LGPL - original licence link has changed is not relivant.
5515  *
5516  * Fork - LGPL
5517  * <script type="text/javascript">
5518  */
5519  
5520
5521 /**
5522  * @class Roo.grid.ColumnModel
5523  * @extends Roo.util.Observable
5524  * This is the default implementation of a ColumnModel used by the Grid. It defines
5525  * the columns in the grid.
5526  * <br>Usage:<br>
5527  <pre><code>
5528  var colModel = new Roo.grid.ColumnModel([
5529         {header: "Ticker", width: 60, sortable: true, locked: true},
5530         {header: "Company Name", width: 150, sortable: true},
5531         {header: "Market Cap.", width: 100, sortable: true},
5532         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5533         {header: "Employees", width: 100, sortable: true, resizable: false}
5534  ]);
5535  </code></pre>
5536  * <p>
5537  
5538  * The config options listed for this class are options which may appear in each
5539  * individual column definition.
5540  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5541  * @constructor
5542  * @param {Object} config An Array of column config objects. See this class's
5543  * config objects for details.
5544 */
5545 Roo.grid.ColumnModel = function(config){
5546         /**
5547      * The config passed into the constructor
5548      */
5549     this.config = config;
5550     this.lookup = {};
5551
5552     // if no id, create one
5553     // if the column does not have a dataIndex mapping,
5554     // map it to the order it is in the config
5555     for(var i = 0, len = config.length; i < len; i++){
5556         var c = config[i];
5557         if(typeof c.dataIndex == "undefined"){
5558             c.dataIndex = i;
5559         }
5560         if(typeof c.renderer == "string"){
5561             c.renderer = Roo.util.Format[c.renderer];
5562         }
5563         if(typeof c.id == "undefined"){
5564             c.id = Roo.id();
5565         }
5566         if(c.editor && c.editor.xtype){
5567             c.editor  = Roo.factory(c.editor, Roo.grid);
5568         }
5569         if(c.editor && c.editor.isFormField){
5570             c.editor = new Roo.grid.GridEditor(c.editor);
5571         }
5572         this.lookup[c.id] = c;
5573     }
5574
5575     /**
5576      * The width of columns which have no width specified (defaults to 100)
5577      * @type Number
5578      */
5579     this.defaultWidth = 100;
5580
5581     /**
5582      * Default sortable of columns which have no sortable specified (defaults to false)
5583      * @type Boolean
5584      */
5585     this.defaultSortable = false;
5586
5587     this.addEvents({
5588         /**
5589              * @event widthchange
5590              * Fires when the width of a column changes.
5591              * @param {ColumnModel} this
5592              * @param {Number} columnIndex The column index
5593              * @param {Number} newWidth The new width
5594              */
5595             "widthchange": true,
5596         /**
5597              * @event headerchange
5598              * Fires when the text of a header changes.
5599              * @param {ColumnModel} this
5600              * @param {Number} columnIndex The column index
5601              * @param {Number} newText The new header text
5602              */
5603             "headerchange": true,
5604         /**
5605              * @event hiddenchange
5606              * Fires when a column is hidden or "unhidden".
5607              * @param {ColumnModel} this
5608              * @param {Number} columnIndex The column index
5609              * @param {Boolean} hidden true if hidden, false otherwise
5610              */
5611             "hiddenchange": true,
5612             /**
5613          * @event columnmoved
5614          * Fires when a column is moved.
5615          * @param {ColumnModel} this
5616          * @param {Number} oldIndex
5617          * @param {Number} newIndex
5618          */
5619         "columnmoved" : true,
5620         /**
5621          * @event columlockchange
5622          * Fires when a column's locked state is changed
5623          * @param {ColumnModel} this
5624          * @param {Number} colIndex
5625          * @param {Boolean} locked true if locked
5626          */
5627         "columnlockchange" : true
5628     });
5629     Roo.grid.ColumnModel.superclass.constructor.call(this);
5630 };
5631 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5632     /**
5633      * @cfg {String} header The header text to display in the Grid view.
5634      */
5635     /**
5636      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5637      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5638      * specified, the column's index is used as an index into the Record's data Array.
5639      */
5640     /**
5641      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5642      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5643      */
5644     /**
5645      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5646      * Defaults to the value of the {@link #defaultSortable} property.
5647      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5648      */
5649     /**
5650      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5651      */
5652     /**
5653      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5654      */
5655     /**
5656      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5657      */
5658     /**
5659      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5660      */
5661     /**
5662      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5663      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5664      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5665      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5666      */
5667        /**
5668      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5669      */
5670     /**
5671      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5672      */
5673     /**
5674      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
5675      */
5676     /**
5677      * @cfg {String} cursor (Optional)
5678      */
5679     /**
5680      * @cfg {String} tooltip (Optional)
5681      */
5682     /**
5683      * @cfg {Number} xs (Optional)
5684      */
5685     /**
5686      * @cfg {Number} sm (Optional)
5687      */
5688     /**
5689      * @cfg {Number} md (Optional)
5690      */
5691     /**
5692      * @cfg {Number} lg (Optional)
5693      */
5694     /**
5695      * Returns the id of the column at the specified index.
5696      * @param {Number} index The column index
5697      * @return {String} the id
5698      */
5699     getColumnId : function(index){
5700         return this.config[index].id;
5701     },
5702
5703     /**
5704      * Returns the column for a specified id.
5705      * @param {String} id The column id
5706      * @return {Object} the column
5707      */
5708     getColumnById : function(id){
5709         return this.lookup[id];
5710     },
5711
5712     
5713     /**
5714      * Returns the column for a specified dataIndex.
5715      * @param {String} dataIndex The column dataIndex
5716      * @return {Object|Boolean} the column or false if not found
5717      */
5718     getColumnByDataIndex: function(dataIndex){
5719         var index = this.findColumnIndex(dataIndex);
5720         return index > -1 ? this.config[index] : false;
5721     },
5722     
5723     /**
5724      * Returns the index for a specified column id.
5725      * @param {String} id The column id
5726      * @return {Number} the index, or -1 if not found
5727      */
5728     getIndexById : function(id){
5729         for(var i = 0, len = this.config.length; i < len; i++){
5730             if(this.config[i].id == id){
5731                 return i;
5732             }
5733         }
5734         return -1;
5735     },
5736     
5737     /**
5738      * Returns the index for a specified column dataIndex.
5739      * @param {String} dataIndex The column dataIndex
5740      * @return {Number} the index, or -1 if not found
5741      */
5742     
5743     findColumnIndex : function(dataIndex){
5744         for(var i = 0, len = this.config.length; i < len; i++){
5745             if(this.config[i].dataIndex == dataIndex){
5746                 return i;
5747             }
5748         }
5749         return -1;
5750     },
5751     
5752     
5753     moveColumn : function(oldIndex, newIndex){
5754         var c = this.config[oldIndex];
5755         this.config.splice(oldIndex, 1);
5756         this.config.splice(newIndex, 0, c);
5757         this.dataMap = null;
5758         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5759     },
5760
5761     isLocked : function(colIndex){
5762         return this.config[colIndex].locked === true;
5763     },
5764
5765     setLocked : function(colIndex, value, suppressEvent){
5766         if(this.isLocked(colIndex) == value){
5767             return;
5768         }
5769         this.config[colIndex].locked = value;
5770         if(!suppressEvent){
5771             this.fireEvent("columnlockchange", this, colIndex, value);
5772         }
5773     },
5774
5775     getTotalLockedWidth : function(){
5776         var totalWidth = 0;
5777         for(var i = 0; i < this.config.length; i++){
5778             if(this.isLocked(i) && !this.isHidden(i)){
5779                 this.totalWidth += this.getColumnWidth(i);
5780             }
5781         }
5782         return totalWidth;
5783     },
5784
5785     getLockedCount : function(){
5786         for(var i = 0, len = this.config.length; i < len; i++){
5787             if(!this.isLocked(i)){
5788                 return i;
5789             }
5790         }
5791         
5792         return this.config.length;
5793     },
5794
5795     /**
5796      * Returns the number of columns.
5797      * @return {Number}
5798      */
5799     getColumnCount : function(visibleOnly){
5800         if(visibleOnly === true){
5801             var c = 0;
5802             for(var i = 0, len = this.config.length; i < len; i++){
5803                 if(!this.isHidden(i)){
5804                     c++;
5805                 }
5806             }
5807             return c;
5808         }
5809         return this.config.length;
5810     },
5811
5812     /**
5813      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5814      * @param {Function} fn
5815      * @param {Object} scope (optional)
5816      * @return {Array} result
5817      */
5818     getColumnsBy : function(fn, scope){
5819         var r = [];
5820         for(var i = 0, len = this.config.length; i < len; i++){
5821             var c = this.config[i];
5822             if(fn.call(scope||this, c, i) === true){
5823                 r[r.length] = c;
5824             }
5825         }
5826         return r;
5827     },
5828
5829     /**
5830      * Returns true if the specified column is sortable.
5831      * @param {Number} col The column index
5832      * @return {Boolean}
5833      */
5834     isSortable : function(col){
5835         if(typeof this.config[col].sortable == "undefined"){
5836             return this.defaultSortable;
5837         }
5838         return this.config[col].sortable;
5839     },
5840
5841     /**
5842      * Returns the rendering (formatting) function defined for the column.
5843      * @param {Number} col The column index.
5844      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5845      */
5846     getRenderer : function(col){
5847         if(!this.config[col].renderer){
5848             return Roo.grid.ColumnModel.defaultRenderer;
5849         }
5850         return this.config[col].renderer;
5851     },
5852
5853     /**
5854      * Sets the rendering (formatting) function for a column.
5855      * @param {Number} col The column index
5856      * @param {Function} fn The function to use to process the cell's raw data
5857      * to return HTML markup for the grid view. The render function is called with
5858      * the following parameters:<ul>
5859      * <li>Data value.</li>
5860      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5861      * <li>css A CSS style string to apply to the table cell.</li>
5862      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5863      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5864      * <li>Row index</li>
5865      * <li>Column index</li>
5866      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5867      */
5868     setRenderer : function(col, fn){
5869         this.config[col].renderer = fn;
5870     },
5871
5872     /**
5873      * Returns the width for the specified column.
5874      * @param {Number} col The column index
5875      * @return {Number}
5876      */
5877     getColumnWidth : function(col){
5878         return this.config[col].width * 1 || this.defaultWidth;
5879     },
5880
5881     /**
5882      * Sets the width for a column.
5883      * @param {Number} col The column index
5884      * @param {Number} width The new width
5885      */
5886     setColumnWidth : function(col, width, suppressEvent){
5887         this.config[col].width = width;
5888         this.totalWidth = null;
5889         if(!suppressEvent){
5890              this.fireEvent("widthchange", this, col, width);
5891         }
5892     },
5893
5894     /**
5895      * Returns the total width of all columns.
5896      * @param {Boolean} includeHidden True to include hidden column widths
5897      * @return {Number}
5898      */
5899     getTotalWidth : function(includeHidden){
5900         if(!this.totalWidth){
5901             this.totalWidth = 0;
5902             for(var i = 0, len = this.config.length; i < len; i++){
5903                 if(includeHidden || !this.isHidden(i)){
5904                     this.totalWidth += this.getColumnWidth(i);
5905                 }
5906             }
5907         }
5908         return this.totalWidth;
5909     },
5910
5911     /**
5912      * Returns the header for the specified column.
5913      * @param {Number} col The column index
5914      * @return {String}
5915      */
5916     getColumnHeader : function(col){
5917         return this.config[col].header;
5918     },
5919
5920     /**
5921      * Sets the header for a column.
5922      * @param {Number} col The column index
5923      * @param {String} header The new header
5924      */
5925     setColumnHeader : function(col, header){
5926         this.config[col].header = header;
5927         this.fireEvent("headerchange", this, col, header);
5928     },
5929
5930     /**
5931      * Returns the tooltip for the specified column.
5932      * @param {Number} col The column index
5933      * @return {String}
5934      */
5935     getColumnTooltip : function(col){
5936             return this.config[col].tooltip;
5937     },
5938     /**
5939      * Sets the tooltip for a column.
5940      * @param {Number} col The column index
5941      * @param {String} tooltip The new tooltip
5942      */
5943     setColumnTooltip : function(col, tooltip){
5944             this.config[col].tooltip = tooltip;
5945     },
5946
5947     /**
5948      * Returns the dataIndex for the specified column.
5949      * @param {Number} col The column index
5950      * @return {Number}
5951      */
5952     getDataIndex : function(col){
5953         return this.config[col].dataIndex;
5954     },
5955
5956     /**
5957      * Sets the dataIndex for a column.
5958      * @param {Number} col The column index
5959      * @param {Number} dataIndex The new dataIndex
5960      */
5961     setDataIndex : function(col, dataIndex){
5962         this.config[col].dataIndex = dataIndex;
5963     },
5964
5965     
5966     
5967     /**
5968      * Returns true if the cell is editable.
5969      * @param {Number} colIndex The column index
5970      * @param {Number} rowIndex The row index - this is nto actually used..?
5971      * @return {Boolean}
5972      */
5973     isCellEditable : function(colIndex, rowIndex){
5974         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5975     },
5976
5977     /**
5978      * Returns the editor defined for the cell/column.
5979      * return false or null to disable editing.
5980      * @param {Number} colIndex The column index
5981      * @param {Number} rowIndex The row index
5982      * @return {Object}
5983      */
5984     getCellEditor : function(colIndex, rowIndex){
5985         return this.config[colIndex].editor;
5986     },
5987
5988     /**
5989      * Sets if a column is editable.
5990      * @param {Number} col The column index
5991      * @param {Boolean} editable True if the column is editable
5992      */
5993     setEditable : function(col, editable){
5994         this.config[col].editable = editable;
5995     },
5996
5997
5998     /**
5999      * Returns true if the column is hidden.
6000      * @param {Number} colIndex The column index
6001      * @return {Boolean}
6002      */
6003     isHidden : function(colIndex){
6004         return this.config[colIndex].hidden;
6005     },
6006
6007
6008     /**
6009      * Returns true if the column width cannot be changed
6010      */
6011     isFixed : function(colIndex){
6012         return this.config[colIndex].fixed;
6013     },
6014
6015     /**
6016      * Returns true if the column can be resized
6017      * @return {Boolean}
6018      */
6019     isResizable : function(colIndex){
6020         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
6021     },
6022     /**
6023      * Sets if a column is hidden.
6024      * @param {Number} colIndex The column index
6025      * @param {Boolean} hidden True if the column is hidden
6026      */
6027     setHidden : function(colIndex, hidden){
6028         this.config[colIndex].hidden = hidden;
6029         this.totalWidth = null;
6030         this.fireEvent("hiddenchange", this, colIndex, hidden);
6031     },
6032
6033     /**
6034      * Sets the editor for a column.
6035      * @param {Number} col The column index
6036      * @param {Object} editor The editor object
6037      */
6038     setEditor : function(col, editor){
6039         this.config[col].editor = editor;
6040     }
6041 });
6042
6043 Roo.grid.ColumnModel.defaultRenderer = function(value)
6044 {
6045     if(typeof value == "object") {
6046         return value;
6047     }
6048         if(typeof value == "string" && value.length < 1){
6049             return "&#160;";
6050         }
6051     
6052         return String.format("{0}", value);
6053 };
6054
6055 // Alias for backwards compatibility
6056 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
6057 /*
6058  * Based on:
6059  * Ext JS Library 1.1.1
6060  * Copyright(c) 2006-2007, Ext JS, LLC.
6061  *
6062  * Originally Released Under LGPL - original licence link has changed is not relivant.
6063  *
6064  * Fork - LGPL
6065  * <script type="text/javascript">
6066  */
6067  
6068 /**
6069  * @class Roo.LoadMask
6070  * A simple utility class for generically masking elements while loading data.  If the element being masked has
6071  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
6072  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
6073  * element's UpdateManager load indicator and will be destroyed after the initial load.
6074  * @constructor
6075  * Create a new LoadMask
6076  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
6077  * @param {Object} config The config object
6078  */
6079 Roo.LoadMask = function(el, config){
6080     this.el = Roo.get(el);
6081     Roo.apply(this, config);
6082     if(this.store){
6083         this.store.on('beforeload', this.onBeforeLoad, this);
6084         this.store.on('load', this.onLoad, this);
6085         this.store.on('loadexception', this.onLoadException, this);
6086         this.removeMask = false;
6087     }else{
6088         var um = this.el.getUpdateManager();
6089         um.showLoadIndicator = false; // disable the default indicator
6090         um.on('beforeupdate', this.onBeforeLoad, this);
6091         um.on('update', this.onLoad, this);
6092         um.on('failure', this.onLoad, this);
6093         this.removeMask = true;
6094     }
6095 };
6096
6097 Roo.LoadMask.prototype = {
6098     /**
6099      * @cfg {Boolean} removeMask
6100      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
6101      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
6102      */
6103     /**
6104      * @cfg {String} msg
6105      * The text to display in a centered loading message box (defaults to 'Loading...')
6106      */
6107     msg : 'Loading...',
6108     /**
6109      * @cfg {String} msgCls
6110      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
6111      */
6112     msgCls : 'x-mask-loading',
6113
6114     /**
6115      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
6116      * @type Boolean
6117      */
6118     disabled: false,
6119
6120     /**
6121      * Disables the mask to prevent it from being displayed
6122      */
6123     disable : function(){
6124        this.disabled = true;
6125     },
6126
6127     /**
6128      * Enables the mask so that it can be displayed
6129      */
6130     enable : function(){
6131         this.disabled = false;
6132     },
6133     
6134     onLoadException : function()
6135     {
6136         Roo.log(arguments);
6137         
6138         if (typeof(arguments[3]) != 'undefined') {
6139             Roo.MessageBox.alert("Error loading",arguments[3]);
6140         } 
6141         /*
6142         try {
6143             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
6144                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
6145             }   
6146         } catch(e) {
6147             
6148         }
6149         */
6150     
6151         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6152     },
6153     // private
6154     onLoad : function()
6155     {
6156         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
6157     },
6158
6159     // private
6160     onBeforeLoad : function(){
6161         if(!this.disabled){
6162             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
6163         }
6164     },
6165
6166     // private
6167     destroy : function(){
6168         if(this.store){
6169             this.store.un('beforeload', this.onBeforeLoad, this);
6170             this.store.un('load', this.onLoad, this);
6171             this.store.un('loadexception', this.onLoadException, this);
6172         }else{
6173             var um = this.el.getUpdateManager();
6174             um.un('beforeupdate', this.onBeforeLoad, this);
6175             um.un('update', this.onLoad, this);
6176             um.un('failure', this.onLoad, this);
6177         }
6178     }
6179 };/*
6180  * - LGPL
6181  *
6182  * table
6183  * 
6184  */
6185
6186 /**
6187  * @class Roo.bootstrap.Table
6188  * @extends Roo.bootstrap.Component
6189  * Bootstrap Table class
6190  * @cfg {String} cls table class
6191  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
6192  * @cfg {String} bgcolor Specifies the background color for a table
6193  * @cfg {Number} border Specifies whether the table cells should have borders or not
6194  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
6195  * @cfg {Number} cellspacing Specifies the space between cells
6196  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
6197  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
6198  * @cfg {String} sortable Specifies that the table should be sortable
6199  * @cfg {String} summary Specifies a summary of the content of a table
6200  * @cfg {Number} width Specifies the width of a table
6201  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
6202  * 
6203  * @cfg {boolean} striped Should the rows be alternative striped
6204  * @cfg {boolean} bordered Add borders to the table
6205  * @cfg {boolean} hover Add hover highlighting
6206  * @cfg {boolean} condensed Format condensed
6207  * @cfg {boolean} responsive Format condensed
6208  * @cfg {Boolean} loadMask (true|false) default false
6209  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
6210  * @cfg {Boolean} headerShow (true|false) generate thead, default true
6211  * @cfg {Boolean} rowSelection (true|false) default false
6212  * @cfg {Boolean} cellSelection (true|false) default false
6213  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
6214  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
6215  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
6216  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
6217  
6218  * 
6219  * @constructor
6220  * Create a new Table
6221  * @param {Object} config The config object
6222  */
6223
6224 Roo.bootstrap.Table = function(config){
6225     Roo.bootstrap.Table.superclass.constructor.call(this, config);
6226     
6227   
6228     
6229     // BC...
6230     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
6231     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
6232     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
6233     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
6234     
6235     this.sm = this.sm || {xtype: 'RowSelectionModel'};
6236     if (this.sm) {
6237         this.sm.grid = this;
6238         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
6239         this.sm = this.selModel;
6240         this.sm.xmodule = this.xmodule || false;
6241     }
6242     
6243     if (this.cm && typeof(this.cm.config) == 'undefined') {
6244         this.colModel = new Roo.grid.ColumnModel(this.cm);
6245         this.cm = this.colModel;
6246         this.cm.xmodule = this.xmodule || false;
6247     }
6248     if (this.store) {
6249         this.store= Roo.factory(this.store, Roo.data);
6250         this.ds = this.store;
6251         this.ds.xmodule = this.xmodule || false;
6252          
6253     }
6254     if (this.footer && this.store) {
6255         this.footer.dataSource = this.ds;
6256         this.footer = Roo.factory(this.footer);
6257     }
6258     
6259     /** @private */
6260     this.addEvents({
6261         /**
6262          * @event cellclick
6263          * Fires when a cell is clicked
6264          * @param {Roo.bootstrap.Table} this
6265          * @param {Roo.Element} el
6266          * @param {Number} rowIndex
6267          * @param {Number} columnIndex
6268          * @param {Roo.EventObject} e
6269          */
6270         "cellclick" : true,
6271         /**
6272          * @event celldblclick
6273          * Fires when a cell is double clicked
6274          * @param {Roo.bootstrap.Table} this
6275          * @param {Roo.Element} el
6276          * @param {Number} rowIndex
6277          * @param {Number} columnIndex
6278          * @param {Roo.EventObject} e
6279          */
6280         "celldblclick" : true,
6281         /**
6282          * @event rowclick
6283          * Fires when a row is clicked
6284          * @param {Roo.bootstrap.Table} this
6285          * @param {Roo.Element} el
6286          * @param {Number} rowIndex
6287          * @param {Roo.EventObject} e
6288          */
6289         "rowclick" : true,
6290         /**
6291          * @event rowdblclick
6292          * Fires when a row is double clicked
6293          * @param {Roo.bootstrap.Table} this
6294          * @param {Roo.Element} el
6295          * @param {Number} rowIndex
6296          * @param {Roo.EventObject} e
6297          */
6298         "rowdblclick" : true,
6299         /**
6300          * @event mouseover
6301          * Fires when a mouseover occur
6302          * @param {Roo.bootstrap.Table} this
6303          * @param {Roo.Element} el
6304          * @param {Number} rowIndex
6305          * @param {Number} columnIndex
6306          * @param {Roo.EventObject} e
6307          */
6308         "mouseover" : true,
6309         /**
6310          * @event mouseout
6311          * Fires when a mouseout occur
6312          * @param {Roo.bootstrap.Table} this
6313          * @param {Roo.Element} el
6314          * @param {Number} rowIndex
6315          * @param {Number} columnIndex
6316          * @param {Roo.EventObject} e
6317          */
6318         "mouseout" : true,
6319         /**
6320          * @event rowclass
6321          * Fires when a row is rendered, so you can change add a style to it.
6322          * @param {Roo.bootstrap.Table} this
6323          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
6324          */
6325         'rowclass' : true,
6326           /**
6327          * @event rowsrendered
6328          * Fires when all the  rows have been rendered
6329          * @param {Roo.bootstrap.Table} this
6330          */
6331         'rowsrendered' : true,
6332         /**
6333          * @event contextmenu
6334          * The raw contextmenu event for the entire grid.
6335          * @param {Roo.EventObject} e
6336          */
6337         "contextmenu" : true,
6338         /**
6339          * @event rowcontextmenu
6340          * Fires when a row is right clicked
6341          * @param {Roo.bootstrap.Table} this
6342          * @param {Number} rowIndex
6343          * @param {Roo.EventObject} e
6344          */
6345         "rowcontextmenu" : true,
6346         /**
6347          * @event cellcontextmenu
6348          * Fires when a cell is right clicked
6349          * @param {Roo.bootstrap.Table} this
6350          * @param {Number} rowIndex
6351          * @param {Number} cellIndex
6352          * @param {Roo.EventObject} e
6353          */
6354          "cellcontextmenu" : true,
6355          /**
6356          * @event headercontextmenu
6357          * Fires when a header is right clicked
6358          * @param {Roo.bootstrap.Table} this
6359          * @param {Number} columnIndex
6360          * @param {Roo.EventObject} e
6361          */
6362         "headercontextmenu" : true
6363     });
6364 };
6365
6366 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
6367     
6368     cls: false,
6369     align: false,
6370     bgcolor: false,
6371     border: false,
6372     cellpadding: false,
6373     cellspacing: false,
6374     frame: false,
6375     rules: false,
6376     sortable: false,
6377     summary: false,
6378     width: false,
6379     striped : false,
6380     scrollBody : false,
6381     bordered: false,
6382     hover:  false,
6383     condensed : false,
6384     responsive : false,
6385     sm : false,
6386     cm : false,
6387     store : false,
6388     loadMask : false,
6389     footerShow : true,
6390     headerShow : true,
6391   
6392     rowSelection : false,
6393     cellSelection : false,
6394     layout : false,
6395     
6396     // Roo.Element - the tbody
6397     mainBody: false,
6398     // Roo.Element - thead element
6399     mainHead: false,
6400     
6401     container: false, // used by gridpanel...
6402     
6403     lazyLoad : false,
6404     
6405     CSS : Roo.util.CSS,
6406     
6407     auto_hide_footer : false,
6408     
6409     getAutoCreate : function()
6410     {
6411         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
6412         
6413         cfg = {
6414             tag: 'table',
6415             cls : 'table',
6416             cn : []
6417         };
6418         if (this.scrollBody) {
6419             cfg.cls += ' table-body-fixed';
6420         }    
6421         if (this.striped) {
6422             cfg.cls += ' table-striped';
6423         }
6424         
6425         if (this.hover) {
6426             cfg.cls += ' table-hover';
6427         }
6428         if (this.bordered) {
6429             cfg.cls += ' table-bordered';
6430         }
6431         if (this.condensed) {
6432             cfg.cls += ' table-condensed';
6433         }
6434         if (this.responsive) {
6435             cfg.cls += ' table-responsive';
6436         }
6437         
6438         if (this.cls) {
6439             cfg.cls+=  ' ' +this.cls;
6440         }
6441         
6442         // this lot should be simplifed...
6443         var _t = this;
6444         var cp = [
6445             'align',
6446             'bgcolor',
6447             'border',
6448             'cellpadding',
6449             'cellspacing',
6450             'frame',
6451             'rules',
6452             'sortable',
6453             'summary',
6454             'width'
6455         ].forEach(function(k) {
6456             if (_t[k]) {
6457                 cfg[k] = _t[k];
6458             }
6459         });
6460         
6461         
6462         if (this.layout) {
6463             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6464         }
6465         
6466         if(this.store || this.cm){
6467             if(this.headerShow){
6468                 cfg.cn.push(this.renderHeader());
6469             }
6470             
6471             cfg.cn.push(this.renderBody());
6472             
6473             if(this.footerShow){
6474                 cfg.cn.push(this.renderFooter());
6475             }
6476             // where does this come from?
6477             //cfg.cls+=  ' TableGrid';
6478         }
6479         
6480         return { cn : [ cfg ] };
6481     },
6482     
6483     initEvents : function()
6484     {   
6485         if(!this.store || !this.cm){
6486             return;
6487         }
6488         if (this.selModel) {
6489             this.selModel.initEvents();
6490         }
6491         
6492         
6493         //Roo.log('initEvents with ds!!!!');
6494         
6495         this.mainBody = this.el.select('tbody', true).first();
6496         this.mainHead = this.el.select('thead', true).first();
6497         this.mainFoot = this.el.select('tfoot', true).first();
6498         
6499         
6500         
6501         var _this = this;
6502         
6503         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6504             e.on('click', _this.sort, _this);
6505         });
6506         
6507         this.mainBody.on("click", this.onClick, this);
6508         this.mainBody.on("dblclick", this.onDblClick, this);
6509         
6510         // why is this done????? = it breaks dialogs??
6511         //this.parent().el.setStyle('position', 'relative');
6512         
6513         
6514         if (this.footer) {
6515             this.footer.parentId = this.id;
6516             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6517             
6518             if(this.lazyLoad){
6519                 this.el.select('tfoot tr td').first().addClass('hide');
6520             }
6521         } 
6522         
6523         if(this.loadMask) {
6524             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6525         }
6526         
6527         this.store.on('load', this.onLoad, this);
6528         this.store.on('beforeload', this.onBeforeLoad, this);
6529         this.store.on('update', this.onUpdate, this);
6530         this.store.on('add', this.onAdd, this);
6531         this.store.on("clear", this.clear, this);
6532         
6533         this.el.on("contextmenu", this.onContextMenu, this);
6534         
6535         this.mainBody.on('scroll', this.onBodyScroll, this);
6536         
6537         this.cm.on("headerchange", this.onHeaderChange, this);
6538         
6539         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
6540         
6541     },
6542     
6543     onContextMenu : function(e, t)
6544     {
6545         this.processEvent("contextmenu", e);
6546     },
6547     
6548     processEvent : function(name, e)
6549     {
6550         if (name != 'touchstart' ) {
6551             this.fireEvent(name, e);    
6552         }
6553         
6554         var t = e.getTarget();
6555         
6556         var cell = Roo.get(t);
6557         
6558         if(!cell){
6559             return;
6560         }
6561         
6562         if(cell.findParent('tfoot', false, true)){
6563             return;
6564         }
6565         
6566         if(cell.findParent('thead', false, true)){
6567             
6568             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6569                 cell = Roo.get(t).findParent('th', false, true);
6570                 if (!cell) {
6571                     Roo.log("failed to find th in thead?");
6572                     Roo.log(e.getTarget());
6573                     return;
6574                 }
6575             }
6576             
6577             var cellIndex = cell.dom.cellIndex;
6578             
6579             var ename = name == 'touchstart' ? 'click' : name;
6580             this.fireEvent("header" + ename, this, cellIndex, e);
6581             
6582             return;
6583         }
6584         
6585         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6586             cell = Roo.get(t).findParent('td', false, true);
6587             if (!cell) {
6588                 Roo.log("failed to find th in tbody?");
6589                 Roo.log(e.getTarget());
6590                 return;
6591             }
6592         }
6593         
6594         var row = cell.findParent('tr', false, true);
6595         var cellIndex = cell.dom.cellIndex;
6596         var rowIndex = row.dom.rowIndex - 1;
6597         
6598         if(row !== false){
6599             
6600             this.fireEvent("row" + name, this, rowIndex, e);
6601             
6602             if(cell !== false){
6603             
6604                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6605             }
6606         }
6607         
6608     },
6609     
6610     onMouseover : function(e, el)
6611     {
6612         var cell = Roo.get(el);
6613         
6614         if(!cell){
6615             return;
6616         }
6617         
6618         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6619             cell = cell.findParent('td', false, true);
6620         }
6621         
6622         var row = cell.findParent('tr', false, true);
6623         var cellIndex = cell.dom.cellIndex;
6624         var rowIndex = row.dom.rowIndex - 1; // start from 0
6625         
6626         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6627         
6628     },
6629     
6630     onMouseout : function(e, el)
6631     {
6632         var cell = Roo.get(el);
6633         
6634         if(!cell){
6635             return;
6636         }
6637         
6638         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6639             cell = cell.findParent('td', false, true);
6640         }
6641         
6642         var row = cell.findParent('tr', false, true);
6643         var cellIndex = cell.dom.cellIndex;
6644         var rowIndex = row.dom.rowIndex - 1; // start from 0
6645         
6646         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6647         
6648     },
6649     
6650     onClick : function(e, el)
6651     {
6652         var cell = Roo.get(el);
6653         
6654         if(!cell || (!this.cellSelection && !this.rowSelection)){
6655             return;
6656         }
6657         
6658         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6659             cell = cell.findParent('td', false, true);
6660         }
6661         
6662         if(!cell || typeof(cell) == 'undefined'){
6663             return;
6664         }
6665         
6666         var row = cell.findParent('tr', false, true);
6667         
6668         if(!row || typeof(row) == 'undefined'){
6669             return;
6670         }
6671         
6672         var cellIndex = cell.dom.cellIndex;
6673         var rowIndex = this.getRowIndex(row);
6674         
6675         // why??? - should these not be based on SelectionModel?
6676         if(this.cellSelection){
6677             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6678         }
6679         
6680         if(this.rowSelection){
6681             this.fireEvent('rowclick', this, row, rowIndex, e);
6682         }
6683         
6684         
6685     },
6686         
6687     onDblClick : function(e,el)
6688     {
6689         var cell = Roo.get(el);
6690         
6691         if(!cell || (!this.cellSelection && !this.rowSelection)){
6692             return;
6693         }
6694         
6695         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6696             cell = cell.findParent('td', false, true);
6697         }
6698         
6699         if(!cell || typeof(cell) == 'undefined'){
6700             return;
6701         }
6702         
6703         var row = cell.findParent('tr', false, true);
6704         
6705         if(!row || typeof(row) == 'undefined'){
6706             return;
6707         }
6708         
6709         var cellIndex = cell.dom.cellIndex;
6710         var rowIndex = this.getRowIndex(row);
6711         
6712         if(this.cellSelection){
6713             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6714         }
6715         
6716         if(this.rowSelection){
6717             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6718         }
6719     },
6720     
6721     sort : function(e,el)
6722     {
6723         var col = Roo.get(el);
6724         
6725         if(!col.hasClass('sortable')){
6726             return;
6727         }
6728         
6729         var sort = col.attr('sort');
6730         var dir = 'ASC';
6731         
6732         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6733             dir = 'DESC';
6734         }
6735         
6736         this.store.sortInfo = {field : sort, direction : dir};
6737         
6738         if (this.footer) {
6739             Roo.log("calling footer first");
6740             this.footer.onClick('first');
6741         } else {
6742         
6743             this.store.load({ params : { start : 0 } });
6744         }
6745     },
6746     
6747     renderHeader : function()
6748     {
6749         var header = {
6750             tag: 'thead',
6751             cn : []
6752         };
6753         
6754         var cm = this.cm;
6755         this.totalWidth = 0;
6756         
6757         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6758             
6759             var config = cm.config[i];
6760             
6761             var c = {
6762                 tag: 'th',
6763                 cls : 'x-hcol-' + i,
6764                 style : '',
6765                 html: cm.getColumnHeader(i)
6766             };
6767             
6768             var hh = '';
6769             
6770             if(typeof(config.sortable) != 'undefined' && config.sortable){
6771                 c.cls = 'sortable';
6772                 c.html = '<i class="glyphicon"></i>' + c.html;
6773             }
6774             
6775             if(typeof(config.lgHeader) != 'undefined'){
6776                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6777             }
6778             
6779             if(typeof(config.mdHeader) != 'undefined'){
6780                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6781             }
6782             
6783             if(typeof(config.smHeader) != 'undefined'){
6784                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6785             }
6786             
6787             if(typeof(config.xsHeader) != 'undefined'){
6788                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6789             }
6790             
6791             if(hh.length){
6792                 c.html = hh;
6793             }
6794             
6795             if(typeof(config.tooltip) != 'undefined'){
6796                 c.tooltip = config.tooltip;
6797             }
6798             
6799             if(typeof(config.colspan) != 'undefined'){
6800                 c.colspan = config.colspan;
6801             }
6802             
6803             if(typeof(config.hidden) != 'undefined' && config.hidden){
6804                 c.style += ' display:none;';
6805             }
6806             
6807             if(typeof(config.dataIndex) != 'undefined'){
6808                 c.sort = config.dataIndex;
6809             }
6810             
6811            
6812             
6813             if(typeof(config.align) != 'undefined' && config.align.length){
6814                 c.style += ' text-align:' + config.align + ';';
6815             }
6816             
6817             if(typeof(config.width) != 'undefined'){
6818                 c.style += ' width:' + config.width + 'px;';
6819                 this.totalWidth += config.width;
6820             } else {
6821                 this.totalWidth += 100; // assume minimum of 100 per column?
6822             }
6823             
6824             if(typeof(config.cls) != 'undefined'){
6825                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6826             }
6827             
6828             ['xs','sm','md','lg'].map(function(size){
6829                 
6830                 if(typeof(config[size]) == 'undefined'){
6831                     return;
6832                 }
6833                 
6834                 if (!config[size]) { // 0 = hidden
6835                     c.cls += ' hidden-' + size;
6836                     return;
6837                 }
6838                 
6839                 c.cls += ' col-' + size + '-' + config[size];
6840
6841             });
6842             
6843             header.cn.push(c)
6844         }
6845         
6846         return header;
6847     },
6848     
6849     renderBody : function()
6850     {
6851         var body = {
6852             tag: 'tbody',
6853             cn : [
6854                 {
6855                     tag: 'tr',
6856                     cn : [
6857                         {
6858                             tag : 'td',
6859                             colspan :  this.cm.getColumnCount()
6860                         }
6861                     ]
6862                 }
6863             ]
6864         };
6865         
6866         return body;
6867     },
6868     
6869     renderFooter : function()
6870     {
6871         var footer = {
6872             tag: 'tfoot',
6873             cn : [
6874                 {
6875                     tag: 'tr',
6876                     cn : [
6877                         {
6878                             tag : 'td',
6879                             colspan :  this.cm.getColumnCount()
6880                         }
6881                     ]
6882                 }
6883             ]
6884         };
6885         
6886         return footer;
6887     },
6888     
6889     
6890     
6891     onLoad : function()
6892     {
6893 //        Roo.log('ds onload');
6894         this.clear();
6895         
6896         var _this = this;
6897         var cm = this.cm;
6898         var ds = this.store;
6899         
6900         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6901             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6902             if (_this.store.sortInfo) {
6903                     
6904                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6905                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6906                 }
6907                 
6908                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6909                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6910                 }
6911             }
6912         });
6913         
6914         var tbody =  this.mainBody;
6915               
6916         if(ds.getCount() > 0){
6917             ds.data.each(function(d,rowIndex){
6918                 var row =  this.renderRow(cm, ds, rowIndex);
6919                 
6920                 tbody.createChild(row);
6921                 
6922                 var _this = this;
6923                 
6924                 if(row.cellObjects.length){
6925                     Roo.each(row.cellObjects, function(r){
6926                         _this.renderCellObject(r);
6927                     })
6928                 }
6929                 
6930             }, this);
6931         }
6932         
6933         var tfoot = this.el.select('tfoot', true).first();
6934         
6935         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
6936             
6937             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
6938             
6939             var total = this.ds.getTotalCount();
6940             
6941             if(this.footer.pageSize < total){
6942                 this.mainFoot.show();
6943             }
6944         }
6945         
6946         Roo.each(this.el.select('tbody td', true).elements, function(e){
6947             e.on('mouseover', _this.onMouseover, _this);
6948         });
6949         
6950         Roo.each(this.el.select('tbody td', true).elements, function(e){
6951             e.on('mouseout', _this.onMouseout, _this);
6952         });
6953         this.fireEvent('rowsrendered', this);
6954         
6955         this.autoSize();
6956     },
6957     
6958     
6959     onUpdate : function(ds,record)
6960     {
6961         this.refreshRow(record);
6962         this.autoSize();
6963     },
6964     
6965     onRemove : function(ds, record, index, isUpdate){
6966         if(isUpdate !== true){
6967             this.fireEvent("beforerowremoved", this, index, record);
6968         }
6969         var bt = this.mainBody.dom;
6970         
6971         var rows = this.el.select('tbody > tr', true).elements;
6972         
6973         if(typeof(rows[index]) != 'undefined'){
6974             bt.removeChild(rows[index].dom);
6975         }
6976         
6977 //        if(bt.rows[index]){
6978 //            bt.removeChild(bt.rows[index]);
6979 //        }
6980         
6981         if(isUpdate !== true){
6982             //this.stripeRows(index);
6983             //this.syncRowHeights(index, index);
6984             //this.layout();
6985             this.fireEvent("rowremoved", this, index, record);
6986         }
6987     },
6988     
6989     onAdd : function(ds, records, rowIndex)
6990     {
6991         //Roo.log('on Add called');
6992         // - note this does not handle multiple adding very well..
6993         var bt = this.mainBody.dom;
6994         for (var i =0 ; i < records.length;i++) {
6995             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6996             //Roo.log(records[i]);
6997             //Roo.log(this.store.getAt(rowIndex+i));
6998             this.insertRow(this.store, rowIndex + i, false);
6999             return;
7000         }
7001         
7002     },
7003     
7004     
7005     refreshRow : function(record){
7006         var ds = this.store, index;
7007         if(typeof record == 'number'){
7008             index = record;
7009             record = ds.getAt(index);
7010         }else{
7011             index = ds.indexOf(record);
7012         }
7013         this.insertRow(ds, index, true);
7014         this.autoSize();
7015         this.onRemove(ds, record, index+1, true);
7016         this.autoSize();
7017         //this.syncRowHeights(index, index);
7018         //this.layout();
7019         this.fireEvent("rowupdated", this, index, record);
7020     },
7021     
7022     insertRow : function(dm, rowIndex, isUpdate){
7023         
7024         if(!isUpdate){
7025             this.fireEvent("beforerowsinserted", this, rowIndex);
7026         }
7027             //var s = this.getScrollState();
7028         var row = this.renderRow(this.cm, this.store, rowIndex);
7029         // insert before rowIndex..
7030         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
7031         
7032         var _this = this;
7033                 
7034         if(row.cellObjects.length){
7035             Roo.each(row.cellObjects, function(r){
7036                 _this.renderCellObject(r);
7037             })
7038         }
7039             
7040         if(!isUpdate){
7041             this.fireEvent("rowsinserted", this, rowIndex);
7042             //this.syncRowHeights(firstRow, lastRow);
7043             //this.stripeRows(firstRow);
7044             //this.layout();
7045         }
7046         
7047     },
7048     
7049     
7050     getRowDom : function(rowIndex)
7051     {
7052         var rows = this.el.select('tbody > tr', true).elements;
7053         
7054         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
7055         
7056     },
7057     // returns the object tree for a tr..
7058   
7059     
7060     renderRow : function(cm, ds, rowIndex) 
7061     {
7062         var d = ds.getAt(rowIndex);
7063         
7064         var row = {
7065             tag : 'tr',
7066             cls : 'x-row-' + rowIndex,
7067             cn : []
7068         };
7069             
7070         var cellObjects = [];
7071         
7072         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
7073             var config = cm.config[i];
7074             
7075             var renderer = cm.getRenderer(i);
7076             var value = '';
7077             var id = false;
7078             
7079             if(typeof(renderer) !== 'undefined'){
7080                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
7081             }
7082             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
7083             // and are rendered into the cells after the row is rendered - using the id for the element.
7084             
7085             if(typeof(value) === 'object'){
7086                 id = Roo.id();
7087                 cellObjects.push({
7088                     container : id,
7089                     cfg : value 
7090                 })
7091             }
7092             
7093             var rowcfg = {
7094                 record: d,
7095                 rowIndex : rowIndex,
7096                 colIndex : i,
7097                 rowClass : ''
7098             };
7099
7100             this.fireEvent('rowclass', this, rowcfg);
7101             
7102             var td = {
7103                 tag: 'td',
7104                 cls : rowcfg.rowClass + ' x-col-' + i,
7105                 style: '',
7106                 html: (typeof(value) === 'object') ? '' : value
7107             };
7108             
7109             if (id) {
7110                 td.id = id;
7111             }
7112             
7113             if(typeof(config.colspan) != 'undefined'){
7114                 td.colspan = config.colspan;
7115             }
7116             
7117             if(typeof(config.hidden) != 'undefined' && config.hidden){
7118                 td.style += ' display:none;';
7119             }
7120             
7121             if(typeof(config.align) != 'undefined' && config.align.length){
7122                 td.style += ' text-align:' + config.align + ';';
7123             }
7124             if(typeof(config.valign) != 'undefined' && config.valign.length){
7125                 td.style += ' vertical-align:' + config.valign + ';';
7126             }
7127             
7128             if(typeof(config.width) != 'undefined'){
7129                 td.style += ' width:' +  config.width + 'px;';
7130             }
7131             
7132             if(typeof(config.cursor) != 'undefined'){
7133                 td.style += ' cursor:' +  config.cursor + ';';
7134             }
7135             
7136             if(typeof(config.cls) != 'undefined'){
7137                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
7138             }
7139             
7140             ['xs','sm','md','lg'].map(function(size){
7141                 
7142                 if(typeof(config[size]) == 'undefined'){
7143                     return;
7144                 }
7145                 
7146                 if (!config[size]) { // 0 = hidden
7147                     td.cls += ' hidden-' + size;
7148                     return;
7149                 }
7150                 
7151                 td.cls += ' col-' + size + '-' + config[size];
7152
7153             });
7154             
7155             row.cn.push(td);
7156            
7157         }
7158         
7159         row.cellObjects = cellObjects;
7160         
7161         return row;
7162           
7163     },
7164     
7165     
7166     
7167     onBeforeLoad : function()
7168     {
7169         
7170     },
7171      /**
7172      * Remove all rows
7173      */
7174     clear : function()
7175     {
7176         this.el.select('tbody', true).first().dom.innerHTML = '';
7177     },
7178     /**
7179      * Show or hide a row.
7180      * @param {Number} rowIndex to show or hide
7181      * @param {Boolean} state hide
7182      */
7183     setRowVisibility : function(rowIndex, state)
7184     {
7185         var bt = this.mainBody.dom;
7186         
7187         var rows = this.el.select('tbody > tr', true).elements;
7188         
7189         if(typeof(rows[rowIndex]) == 'undefined'){
7190             return;
7191         }
7192         rows[rowIndex].dom.style.display = state ? '' : 'none';
7193     },
7194     
7195     
7196     getSelectionModel : function(){
7197         if(!this.selModel){
7198             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
7199         }
7200         return this.selModel;
7201     },
7202     /*
7203      * Render the Roo.bootstrap object from renderder
7204      */
7205     renderCellObject : function(r)
7206     {
7207         var _this = this;
7208         
7209         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
7210         
7211         var t = r.cfg.render(r.container);
7212         
7213         if(r.cfg.cn){
7214             Roo.each(r.cfg.cn, function(c){
7215                 var child = {
7216                     container: t.getChildContainer(),
7217                     cfg: c
7218                 };
7219                 _this.renderCellObject(child);
7220             })
7221         }
7222     },
7223     
7224     getRowIndex : function(row)
7225     {
7226         var rowIndex = -1;
7227         
7228         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
7229             if(el != row){
7230                 return;
7231             }
7232             
7233             rowIndex = index;
7234         });
7235         
7236         return rowIndex;
7237     },
7238      /**
7239      * Returns the grid's underlying element = used by panel.Grid
7240      * @return {Element} The element
7241      */
7242     getGridEl : function(){
7243         return this.el;
7244     },
7245      /**
7246      * Forces a resize - used by panel.Grid
7247      * @return {Element} The element
7248      */
7249     autoSize : function()
7250     {
7251         //var ctr = Roo.get(this.container.dom.parentElement);
7252         var ctr = Roo.get(this.el.dom);
7253         
7254         var thd = this.getGridEl().select('thead',true).first();
7255         var tbd = this.getGridEl().select('tbody', true).first();
7256         var tfd = this.getGridEl().select('tfoot', true).first();
7257         
7258         var cw = ctr.getWidth();
7259         
7260         if (tbd) {
7261             
7262             tbd.setSize(ctr.getWidth(),
7263                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
7264             );
7265             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
7266             cw -= barsize;
7267         }
7268         cw = Math.max(cw, this.totalWidth);
7269         this.getGridEl().select('tr',true).setWidth(cw);
7270         // resize 'expandable coloumn?
7271         
7272         return; // we doe not have a view in this design..
7273         
7274     },
7275     onBodyScroll: function()
7276     {
7277         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
7278         if(this.mainHead){
7279             this.mainHead.setStyle({
7280                 'position' : 'relative',
7281                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
7282             });
7283         }
7284         
7285         if(this.lazyLoad){
7286             
7287             var scrollHeight = this.mainBody.dom.scrollHeight;
7288             
7289             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
7290             
7291             var height = this.mainBody.getHeight();
7292             
7293             if(scrollHeight - height == scrollTop) {
7294                 
7295                 var total = this.ds.getTotalCount();
7296                 
7297                 if(this.footer.cursor + this.footer.pageSize < total){
7298                     
7299                     this.footer.ds.load({
7300                         params : {
7301                             start : this.footer.cursor + this.footer.pageSize,
7302                             limit : this.footer.pageSize
7303                         },
7304                         add : true
7305                     });
7306                 }
7307             }
7308             
7309         }
7310     },
7311     
7312     onHeaderChange : function()
7313     {
7314         var header = this.renderHeader();
7315         var table = this.el.select('table', true).first();
7316         
7317         this.mainHead.remove();
7318         this.mainHead = table.createChild(header, this.mainBody, false);
7319     },
7320     
7321     onHiddenChange : function(colModel, colIndex, hidden)
7322     {
7323         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
7324         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
7325         
7326         this.CSS.updateRule(thSelector, "display", "");
7327         this.CSS.updateRule(tdSelector, "display", "");
7328         
7329         if(hidden){
7330             this.CSS.updateRule(thSelector, "display", "none");
7331             this.CSS.updateRule(tdSelector, "display", "none");
7332         }
7333         
7334         this.onHeaderChange();
7335         this.onLoad();
7336     },
7337     
7338     setColumnWidth: function(col_index, width)
7339     {
7340         // width = "md-2 xs-2..."
7341         if(!this.colModel.config[col_index]) {
7342             return;
7343         }
7344         
7345         var w = width.split(" ");
7346         
7347         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
7348         
7349         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
7350         
7351         
7352         for(var j = 0; j < w.length; j++) {
7353             
7354             if(!w[j]) {
7355                 continue;
7356             }
7357             
7358             var size_cls = w[j].split("-");
7359             
7360             if(!Number.isInteger(size_cls[1] * 1)) {
7361                 continue;
7362             }
7363             
7364             if(!this.colModel.config[col_index][size_cls[0]]) {
7365                 continue;
7366             }
7367             
7368             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7369                 continue;
7370             }
7371             
7372             h_row[0].classList.replace(
7373                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7374                 "col-"+size_cls[0]+"-"+size_cls[1]
7375             );
7376             
7377             for(var i = 0; i < rows.length; i++) {
7378                 
7379                 var size_cls = w[j].split("-");
7380                 
7381                 if(!Number.isInteger(size_cls[1] * 1)) {
7382                     continue;
7383                 }
7384                 
7385                 if(!this.colModel.config[col_index][size_cls[0]]) {
7386                     continue;
7387                 }
7388                 
7389                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
7390                     continue;
7391                 }
7392                 
7393                 rows[i].classList.replace(
7394                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
7395                     "col-"+size_cls[0]+"-"+size_cls[1]
7396                 );
7397             }
7398             
7399             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
7400         }
7401     }
7402 });
7403
7404  
7405
7406  /*
7407  * - LGPL
7408  *
7409  * table cell
7410  * 
7411  */
7412
7413 /**
7414  * @class Roo.bootstrap.TableCell
7415  * @extends Roo.bootstrap.Component
7416  * Bootstrap TableCell class
7417  * @cfg {String} html cell contain text
7418  * @cfg {String} cls cell class
7419  * @cfg {String} tag cell tag (td|th) default td
7420  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
7421  * @cfg {String} align Aligns the content in a cell
7422  * @cfg {String} axis Categorizes cells
7423  * @cfg {String} bgcolor Specifies the background color of a cell
7424  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7425  * @cfg {Number} colspan Specifies the number of columns a cell should span
7426  * @cfg {String} headers Specifies one or more header cells a cell is related to
7427  * @cfg {Number} height Sets the height of a cell
7428  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
7429  * @cfg {Number} rowspan Sets the number of rows a cell should span
7430  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
7431  * @cfg {String} valign Vertical aligns the content in a cell
7432  * @cfg {Number} width Specifies the width of a cell
7433  * 
7434  * @constructor
7435  * Create a new TableCell
7436  * @param {Object} config The config object
7437  */
7438
7439 Roo.bootstrap.TableCell = function(config){
7440     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
7441 };
7442
7443 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
7444     
7445     html: false,
7446     cls: false,
7447     tag: false,
7448     abbr: false,
7449     align: false,
7450     axis: false,
7451     bgcolor: false,
7452     charoff: false,
7453     colspan: false,
7454     headers: false,
7455     height: false,
7456     nowrap: false,
7457     rowspan: false,
7458     scope: false,
7459     valign: false,
7460     width: false,
7461     
7462     
7463     getAutoCreate : function(){
7464         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
7465         
7466         cfg = {
7467             tag: 'td'
7468         };
7469         
7470         if(this.tag){
7471             cfg.tag = this.tag;
7472         }
7473         
7474         if (this.html) {
7475             cfg.html=this.html
7476         }
7477         if (this.cls) {
7478             cfg.cls=this.cls
7479         }
7480         if (this.abbr) {
7481             cfg.abbr=this.abbr
7482         }
7483         if (this.align) {
7484             cfg.align=this.align
7485         }
7486         if (this.axis) {
7487             cfg.axis=this.axis
7488         }
7489         if (this.bgcolor) {
7490             cfg.bgcolor=this.bgcolor
7491         }
7492         if (this.charoff) {
7493             cfg.charoff=this.charoff
7494         }
7495         if (this.colspan) {
7496             cfg.colspan=this.colspan
7497         }
7498         if (this.headers) {
7499             cfg.headers=this.headers
7500         }
7501         if (this.height) {
7502             cfg.height=this.height
7503         }
7504         if (this.nowrap) {
7505             cfg.nowrap=this.nowrap
7506         }
7507         if (this.rowspan) {
7508             cfg.rowspan=this.rowspan
7509         }
7510         if (this.scope) {
7511             cfg.scope=this.scope
7512         }
7513         if (this.valign) {
7514             cfg.valign=this.valign
7515         }
7516         if (this.width) {
7517             cfg.width=this.width
7518         }
7519         
7520         
7521         return cfg;
7522     }
7523    
7524 });
7525
7526  
7527
7528  /*
7529  * - LGPL
7530  *
7531  * table row
7532  * 
7533  */
7534
7535 /**
7536  * @class Roo.bootstrap.TableRow
7537  * @extends Roo.bootstrap.Component
7538  * Bootstrap TableRow class
7539  * @cfg {String} cls row class
7540  * @cfg {String} align Aligns the content in a table row
7541  * @cfg {String} bgcolor Specifies a background color for a table row
7542  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7543  * @cfg {String} valign Vertical aligns the content in a table row
7544  * 
7545  * @constructor
7546  * Create a new TableRow
7547  * @param {Object} config The config object
7548  */
7549
7550 Roo.bootstrap.TableRow = function(config){
7551     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7552 };
7553
7554 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7555     
7556     cls: false,
7557     align: false,
7558     bgcolor: false,
7559     charoff: false,
7560     valign: false,
7561     
7562     getAutoCreate : function(){
7563         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7564         
7565         cfg = {
7566             tag: 'tr'
7567         };
7568             
7569         if(this.cls){
7570             cfg.cls = this.cls;
7571         }
7572         if(this.align){
7573             cfg.align = this.align;
7574         }
7575         if(this.bgcolor){
7576             cfg.bgcolor = this.bgcolor;
7577         }
7578         if(this.charoff){
7579             cfg.charoff = this.charoff;
7580         }
7581         if(this.valign){
7582             cfg.valign = this.valign;
7583         }
7584         
7585         return cfg;
7586     }
7587    
7588 });
7589
7590  
7591
7592  /*
7593  * - LGPL
7594  *
7595  * table body
7596  * 
7597  */
7598
7599 /**
7600  * @class Roo.bootstrap.TableBody
7601  * @extends Roo.bootstrap.Component
7602  * Bootstrap TableBody class
7603  * @cfg {String} cls element class
7604  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7605  * @cfg {String} align Aligns the content inside the element
7606  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7607  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7608  * 
7609  * @constructor
7610  * Create a new TableBody
7611  * @param {Object} config The config object
7612  */
7613
7614 Roo.bootstrap.TableBody = function(config){
7615     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7616 };
7617
7618 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7619     
7620     cls: false,
7621     tag: false,
7622     align: false,
7623     charoff: false,
7624     valign: false,
7625     
7626     getAutoCreate : function(){
7627         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7628         
7629         cfg = {
7630             tag: 'tbody'
7631         };
7632             
7633         if (this.cls) {
7634             cfg.cls=this.cls
7635         }
7636         if(this.tag){
7637             cfg.tag = this.tag;
7638         }
7639         
7640         if(this.align){
7641             cfg.align = this.align;
7642         }
7643         if(this.charoff){
7644             cfg.charoff = this.charoff;
7645         }
7646         if(this.valign){
7647             cfg.valign = this.valign;
7648         }
7649         
7650         return cfg;
7651     }
7652     
7653     
7654 //    initEvents : function()
7655 //    {
7656 //        
7657 //        if(!this.store){
7658 //            return;
7659 //        }
7660 //        
7661 //        this.store = Roo.factory(this.store, Roo.data);
7662 //        this.store.on('load', this.onLoad, this);
7663 //        
7664 //        this.store.load();
7665 //        
7666 //    },
7667 //    
7668 //    onLoad: function () 
7669 //    {   
7670 //        this.fireEvent('load', this);
7671 //    }
7672 //    
7673 //   
7674 });
7675
7676  
7677
7678  /*
7679  * Based on:
7680  * Ext JS Library 1.1.1
7681  * Copyright(c) 2006-2007, Ext JS, LLC.
7682  *
7683  * Originally Released Under LGPL - original licence link has changed is not relivant.
7684  *
7685  * Fork - LGPL
7686  * <script type="text/javascript">
7687  */
7688
7689 // as we use this in bootstrap.
7690 Roo.namespace('Roo.form');
7691  /**
7692  * @class Roo.form.Action
7693  * Internal Class used to handle form actions
7694  * @constructor
7695  * @param {Roo.form.BasicForm} el The form element or its id
7696  * @param {Object} config Configuration options
7697  */
7698
7699  
7700  
7701 // define the action interface
7702 Roo.form.Action = function(form, options){
7703     this.form = form;
7704     this.options = options || {};
7705 };
7706 /**
7707  * Client Validation Failed
7708  * @const 
7709  */
7710 Roo.form.Action.CLIENT_INVALID = 'client';
7711 /**
7712  * Server Validation Failed
7713  * @const 
7714  */
7715 Roo.form.Action.SERVER_INVALID = 'server';
7716  /**
7717  * Connect to Server Failed
7718  * @const 
7719  */
7720 Roo.form.Action.CONNECT_FAILURE = 'connect';
7721 /**
7722  * Reading Data from Server Failed
7723  * @const 
7724  */
7725 Roo.form.Action.LOAD_FAILURE = 'load';
7726
7727 Roo.form.Action.prototype = {
7728     type : 'default',
7729     failureType : undefined,
7730     response : undefined,
7731     result : undefined,
7732
7733     // interface method
7734     run : function(options){
7735
7736     },
7737
7738     // interface method
7739     success : function(response){
7740
7741     },
7742
7743     // interface method
7744     handleResponse : function(response){
7745
7746     },
7747
7748     // default connection failure
7749     failure : function(response){
7750         
7751         this.response = response;
7752         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7753         this.form.afterAction(this, false);
7754     },
7755
7756     processResponse : function(response){
7757         this.response = response;
7758         if(!response.responseText){
7759             return true;
7760         }
7761         this.result = this.handleResponse(response);
7762         return this.result;
7763     },
7764
7765     // utility functions used internally
7766     getUrl : function(appendParams){
7767         var url = this.options.url || this.form.url || this.form.el.dom.action;
7768         if(appendParams){
7769             var p = this.getParams();
7770             if(p){
7771                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7772             }
7773         }
7774         return url;
7775     },
7776
7777     getMethod : function(){
7778         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7779     },
7780
7781     getParams : function(){
7782         var bp = this.form.baseParams;
7783         var p = this.options.params;
7784         if(p){
7785             if(typeof p == "object"){
7786                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7787             }else if(typeof p == 'string' && bp){
7788                 p += '&' + Roo.urlEncode(bp);
7789             }
7790         }else if(bp){
7791             p = Roo.urlEncode(bp);
7792         }
7793         return p;
7794     },
7795
7796     createCallback : function(){
7797         return {
7798             success: this.success,
7799             failure: this.failure,
7800             scope: this,
7801             timeout: (this.form.timeout*1000),
7802             upload: this.form.fileUpload ? this.success : undefined
7803         };
7804     }
7805 };
7806
7807 Roo.form.Action.Submit = function(form, options){
7808     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7809 };
7810
7811 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7812     type : 'submit',
7813
7814     haveProgress : false,
7815     uploadComplete : false,
7816     
7817     // uploadProgress indicator.
7818     uploadProgress : function()
7819     {
7820         if (!this.form.progressUrl) {
7821             return;
7822         }
7823         
7824         if (!this.haveProgress) {
7825             Roo.MessageBox.progress("Uploading", "Uploading");
7826         }
7827         if (this.uploadComplete) {
7828            Roo.MessageBox.hide();
7829            return;
7830         }
7831         
7832         this.haveProgress = true;
7833    
7834         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7835         
7836         var c = new Roo.data.Connection();
7837         c.request({
7838             url : this.form.progressUrl,
7839             params: {
7840                 id : uid
7841             },
7842             method: 'GET',
7843             success : function(req){
7844                //console.log(data);
7845                 var rdata = false;
7846                 var edata;
7847                 try  {
7848                    rdata = Roo.decode(req.responseText)
7849                 } catch (e) {
7850                     Roo.log("Invalid data from server..");
7851                     Roo.log(edata);
7852                     return;
7853                 }
7854                 if (!rdata || !rdata.success) {
7855                     Roo.log(rdata);
7856                     Roo.MessageBox.alert(Roo.encode(rdata));
7857                     return;
7858                 }
7859                 var data = rdata.data;
7860                 
7861                 if (this.uploadComplete) {
7862                    Roo.MessageBox.hide();
7863                    return;
7864                 }
7865                    
7866                 if (data){
7867                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7868                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7869                     );
7870                 }
7871                 this.uploadProgress.defer(2000,this);
7872             },
7873        
7874             failure: function(data) {
7875                 Roo.log('progress url failed ');
7876                 Roo.log(data);
7877             },
7878             scope : this
7879         });
7880            
7881     },
7882     
7883     
7884     run : function()
7885     {
7886         // run get Values on the form, so it syncs any secondary forms.
7887         this.form.getValues();
7888         
7889         var o = this.options;
7890         var method = this.getMethod();
7891         var isPost = method == 'POST';
7892         if(o.clientValidation === false || this.form.isValid()){
7893             
7894             if (this.form.progressUrl) {
7895                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7896                     (new Date() * 1) + '' + Math.random());
7897                     
7898             } 
7899             
7900             
7901             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7902                 form:this.form.el.dom,
7903                 url:this.getUrl(!isPost),
7904                 method: method,
7905                 params:isPost ? this.getParams() : null,
7906                 isUpload: this.form.fileUpload,
7907                 formData : this.form.formData
7908             }));
7909             
7910             this.uploadProgress();
7911
7912         }else if (o.clientValidation !== false){ // client validation failed
7913             this.failureType = Roo.form.Action.CLIENT_INVALID;
7914             this.form.afterAction(this, false);
7915         }
7916     },
7917
7918     success : function(response)
7919     {
7920         this.uploadComplete= true;
7921         if (this.haveProgress) {
7922             Roo.MessageBox.hide();
7923         }
7924         
7925         
7926         var result = this.processResponse(response);
7927         if(result === true || result.success){
7928             this.form.afterAction(this, true);
7929             return;
7930         }
7931         if(result.errors){
7932             this.form.markInvalid(result.errors);
7933             this.failureType = Roo.form.Action.SERVER_INVALID;
7934         }
7935         this.form.afterAction(this, false);
7936     },
7937     failure : function(response)
7938     {
7939         this.uploadComplete= true;
7940         if (this.haveProgress) {
7941             Roo.MessageBox.hide();
7942         }
7943         
7944         this.response = response;
7945         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7946         this.form.afterAction(this, false);
7947     },
7948     
7949     handleResponse : function(response){
7950         if(this.form.errorReader){
7951             var rs = this.form.errorReader.read(response);
7952             var errors = [];
7953             if(rs.records){
7954                 for(var i = 0, len = rs.records.length; i < len; i++) {
7955                     var r = rs.records[i];
7956                     errors[i] = r.data;
7957                 }
7958             }
7959             if(errors.length < 1){
7960                 errors = null;
7961             }
7962             return {
7963                 success : rs.success,
7964                 errors : errors
7965             };
7966         }
7967         var ret = false;
7968         try {
7969             ret = Roo.decode(response.responseText);
7970         } catch (e) {
7971             ret = {
7972                 success: false,
7973                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7974                 errors : []
7975             };
7976         }
7977         return ret;
7978         
7979     }
7980 });
7981
7982
7983 Roo.form.Action.Load = function(form, options){
7984     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7985     this.reader = this.form.reader;
7986 };
7987
7988 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7989     type : 'load',
7990
7991     run : function(){
7992         
7993         Roo.Ajax.request(Roo.apply(
7994                 this.createCallback(), {
7995                     method:this.getMethod(),
7996                     url:this.getUrl(false),
7997                     params:this.getParams()
7998         }));
7999     },
8000
8001     success : function(response){
8002         
8003         var result = this.processResponse(response);
8004         if(result === true || !result.success || !result.data){
8005             this.failureType = Roo.form.Action.LOAD_FAILURE;
8006             this.form.afterAction(this, false);
8007             return;
8008         }
8009         this.form.clearInvalid();
8010         this.form.setValues(result.data);
8011         this.form.afterAction(this, true);
8012     },
8013
8014     handleResponse : function(response){
8015         if(this.form.reader){
8016             var rs = this.form.reader.read(response);
8017             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
8018             return {
8019                 success : rs.success,
8020                 data : data
8021             };
8022         }
8023         return Roo.decode(response.responseText);
8024     }
8025 });
8026
8027 Roo.form.Action.ACTION_TYPES = {
8028     'load' : Roo.form.Action.Load,
8029     'submit' : Roo.form.Action.Submit
8030 };/*
8031  * - LGPL
8032  *
8033  * form
8034  *
8035  */
8036
8037 /**
8038  * @class Roo.bootstrap.Form
8039  * @extends Roo.bootstrap.Component
8040  * Bootstrap Form class
8041  * @cfg {String} method  GET | POST (default POST)
8042  * @cfg {String} labelAlign top | left (default top)
8043  * @cfg {String} align left  | right - for navbars
8044  * @cfg {Boolean} loadMask load mask when submit (default true)
8045
8046  *
8047  * @constructor
8048  * Create a new Form
8049  * @param {Object} config The config object
8050  */
8051
8052
8053 Roo.bootstrap.Form = function(config){
8054     
8055     Roo.bootstrap.Form.superclass.constructor.call(this, config);
8056     
8057     Roo.bootstrap.Form.popover.apply();
8058     
8059     this.addEvents({
8060         /**
8061          * @event clientvalidation
8062          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
8063          * @param {Form} this
8064          * @param {Boolean} valid true if the form has passed client-side validation
8065          */
8066         clientvalidation: true,
8067         /**
8068          * @event beforeaction
8069          * Fires before any action is performed. Return false to cancel the action.
8070          * @param {Form} this
8071          * @param {Action} action The action to be performed
8072          */
8073         beforeaction: true,
8074         /**
8075          * @event actionfailed
8076          * Fires when an action fails.
8077          * @param {Form} this
8078          * @param {Action} action The action that failed
8079          */
8080         actionfailed : true,
8081         /**
8082          * @event actioncomplete
8083          * Fires when an action is completed.
8084          * @param {Form} this
8085          * @param {Action} action The action that completed
8086          */
8087         actioncomplete : true
8088     });
8089 };
8090
8091 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
8092
8093      /**
8094      * @cfg {String} method
8095      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
8096      */
8097     method : 'POST',
8098     /**
8099      * @cfg {String} url
8100      * The URL to use for form actions if one isn't supplied in the action options.
8101      */
8102     /**
8103      * @cfg {Boolean} fileUpload
8104      * Set to true if this form is a file upload.
8105      */
8106
8107     /**
8108      * @cfg {Object} baseParams
8109      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
8110      */
8111
8112     /**
8113      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
8114      */
8115     timeout: 30,
8116     /**
8117      * @cfg {Sting} align (left|right) for navbar forms
8118      */
8119     align : 'left',
8120
8121     // private
8122     activeAction : null,
8123
8124     /**
8125      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
8126      * element by passing it or its id or mask the form itself by passing in true.
8127      * @type Mixed
8128      */
8129     waitMsgTarget : false,
8130
8131     loadMask : true,
8132     
8133     /**
8134      * @cfg {Boolean} errorMask (true|false) default false
8135      */
8136     errorMask : false,
8137     
8138     /**
8139      * @cfg {Number} maskOffset Default 100
8140      */
8141     maskOffset : 100,
8142     
8143     /**
8144      * @cfg {Boolean} maskBody
8145      */
8146     maskBody : false,
8147
8148     getAutoCreate : function(){
8149
8150         var cfg = {
8151             tag: 'form',
8152             method : this.method || 'POST',
8153             id : this.id || Roo.id(),
8154             cls : ''
8155         };
8156         if (this.parent().xtype.match(/^Nav/)) {
8157             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
8158
8159         }
8160
8161         if (this.labelAlign == 'left' ) {
8162             cfg.cls += ' form-horizontal';
8163         }
8164
8165
8166         return cfg;
8167     },
8168     initEvents : function()
8169     {
8170         this.el.on('submit', this.onSubmit, this);
8171         // this was added as random key presses on the form where triggering form submit.
8172         this.el.on('keypress', function(e) {
8173             if (e.getCharCode() != 13) {
8174                 return true;
8175             }
8176             // we might need to allow it for textareas.. and some other items.
8177             // check e.getTarget().
8178
8179             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
8180                 return true;
8181             }
8182
8183             Roo.log("keypress blocked");
8184
8185             e.preventDefault();
8186             return false;
8187         });
8188         
8189     },
8190     // private
8191     onSubmit : function(e){
8192         e.stopEvent();
8193     },
8194
8195      /**
8196      * Returns true if client-side validation on the form is successful.
8197      * @return Boolean
8198      */
8199     isValid : function(){
8200         var items = this.getItems();
8201         var valid = true;
8202         var target = false;
8203         
8204         items.each(function(f){
8205             
8206             if(f.validate()){
8207                 return;
8208             }
8209             
8210             Roo.log('invalid field: ' + f.name);
8211             
8212             valid = false;
8213
8214             if(!target && f.el.isVisible(true)){
8215                 target = f;
8216             }
8217            
8218         });
8219         
8220         if(this.errorMask && !valid){
8221             Roo.bootstrap.Form.popover.mask(this, target);
8222         }
8223         
8224         return valid;
8225     },
8226     
8227     /**
8228      * Returns true if any fields in this form have changed since their original load.
8229      * @return Boolean
8230      */
8231     isDirty : function(){
8232         var dirty = false;
8233         var items = this.getItems();
8234         items.each(function(f){
8235            if(f.isDirty()){
8236                dirty = true;
8237                return false;
8238            }
8239            return true;
8240         });
8241         return dirty;
8242     },
8243      /**
8244      * Performs a predefined action (submit or load) or custom actions you define on this form.
8245      * @param {String} actionName The name of the action type
8246      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
8247      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
8248      * accept other config options):
8249      * <pre>
8250 Property          Type             Description
8251 ----------------  ---------------  ----------------------------------------------------------------------------------
8252 url               String           The url for the action (defaults to the form's url)
8253 method            String           The form method to use (defaults to the form's method, or POST if not defined)
8254 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
8255 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
8256                                    validate the form on the client (defaults to false)
8257      * </pre>
8258      * @return {BasicForm} this
8259      */
8260     doAction : function(action, options){
8261         if(typeof action == 'string'){
8262             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
8263         }
8264         if(this.fireEvent('beforeaction', this, action) !== false){
8265             this.beforeAction(action);
8266             action.run.defer(100, action);
8267         }
8268         return this;
8269     },
8270
8271     // private
8272     beforeAction : function(action){
8273         var o = action.options;
8274         
8275         if(this.loadMask){
8276             
8277             if(this.maskBody){
8278                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
8279             } else {
8280                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8281             }
8282         }
8283         // not really supported yet.. ??
8284
8285         //if(this.waitMsgTarget === true){
8286         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
8287         //}else if(this.waitMsgTarget){
8288         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
8289         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
8290         //}else {
8291         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
8292        // }
8293
8294     },
8295
8296     // private
8297     afterAction : function(action, success){
8298         this.activeAction = null;
8299         var o = action.options;
8300
8301         if(this.loadMask){
8302             
8303             if(this.maskBody){
8304                 Roo.get(document.body).unmask();
8305             } else {
8306                 this.el.unmask();
8307             }
8308         }
8309         
8310         //if(this.waitMsgTarget === true){
8311 //            this.el.unmask();
8312         //}else if(this.waitMsgTarget){
8313         //    this.waitMsgTarget.unmask();
8314         //}else{
8315         //    Roo.MessageBox.updateProgress(1);
8316         //    Roo.MessageBox.hide();
8317        // }
8318         //
8319         if(success){
8320             if(o.reset){
8321                 this.reset();
8322             }
8323             Roo.callback(o.success, o.scope, [this, action]);
8324             this.fireEvent('actioncomplete', this, action);
8325
8326         }else{
8327
8328             // failure condition..
8329             // we have a scenario where updates need confirming.
8330             // eg. if a locking scenario exists..
8331             // we look for { errors : { needs_confirm : true }} in the response.
8332             if (
8333                 (typeof(action.result) != 'undefined')  &&
8334                 (typeof(action.result.errors) != 'undefined')  &&
8335                 (typeof(action.result.errors.needs_confirm) != 'undefined')
8336            ){
8337                 var _t = this;
8338                 Roo.log("not supported yet");
8339                  /*
8340
8341                 Roo.MessageBox.confirm(
8342                     "Change requires confirmation",
8343                     action.result.errorMsg,
8344                     function(r) {
8345                         if (r != 'yes') {
8346                             return;
8347                         }
8348                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
8349                     }
8350
8351                 );
8352                 */
8353
8354
8355                 return;
8356             }
8357
8358             Roo.callback(o.failure, o.scope, [this, action]);
8359             // show an error message if no failed handler is set..
8360             if (!this.hasListener('actionfailed')) {
8361                 Roo.log("need to add dialog support");
8362                 /*
8363                 Roo.MessageBox.alert("Error",
8364                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
8365                         action.result.errorMsg :
8366                         "Saving Failed, please check your entries or try again"
8367                 );
8368                 */
8369             }
8370
8371             this.fireEvent('actionfailed', this, action);
8372         }
8373
8374     },
8375     /**
8376      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
8377      * @param {String} id The value to search for
8378      * @return Field
8379      */
8380     findField : function(id){
8381         var items = this.getItems();
8382         var field = items.get(id);
8383         if(!field){
8384              items.each(function(f){
8385                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
8386                     field = f;
8387                     return false;
8388                 }
8389                 return true;
8390             });
8391         }
8392         return field || null;
8393     },
8394      /**
8395      * Mark fields in this form invalid in bulk.
8396      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
8397      * @return {BasicForm} this
8398      */
8399     markInvalid : function(errors){
8400         if(errors instanceof Array){
8401             for(var i = 0, len = errors.length; i < len; i++){
8402                 var fieldError = errors[i];
8403                 var f = this.findField(fieldError.id);
8404                 if(f){
8405                     f.markInvalid(fieldError.msg);
8406                 }
8407             }
8408         }else{
8409             var field, id;
8410             for(id in errors){
8411                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
8412                     field.markInvalid(errors[id]);
8413                 }
8414             }
8415         }
8416         //Roo.each(this.childForms || [], function (f) {
8417         //    f.markInvalid(errors);
8418         //});
8419
8420         return this;
8421     },
8422
8423     /**
8424      * Set values for fields in this form in bulk.
8425      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
8426      * @return {BasicForm} this
8427      */
8428     setValues : function(values){
8429         if(values instanceof Array){ // array of objects
8430             for(var i = 0, len = values.length; i < len; i++){
8431                 var v = values[i];
8432                 var f = this.findField(v.id);
8433                 if(f){
8434                     f.setValue(v.value);
8435                     if(this.trackResetOnLoad){
8436                         f.originalValue = f.getValue();
8437                     }
8438                 }
8439             }
8440         }else{ // object hash
8441             var field, id;
8442             for(id in values){
8443                 if(typeof values[id] != 'function' && (field = this.findField(id))){
8444
8445                     if (field.setFromData &&
8446                         field.valueField &&
8447                         field.displayField &&
8448                         // combos' with local stores can
8449                         // be queried via setValue()
8450                         // to set their value..
8451                         (field.store && !field.store.isLocal)
8452                         ) {
8453                         // it's a combo
8454                         var sd = { };
8455                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
8456                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
8457                         field.setFromData(sd);
8458
8459                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
8460                         
8461                         field.setFromData(values);
8462                         
8463                     } else {
8464                         field.setValue(values[id]);
8465                     }
8466
8467
8468                     if(this.trackResetOnLoad){
8469                         field.originalValue = field.getValue();
8470                     }
8471                 }
8472             }
8473         }
8474
8475         //Roo.each(this.childForms || [], function (f) {
8476         //    f.setValues(values);
8477         //});
8478
8479         return this;
8480     },
8481
8482     /**
8483      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
8484      * they are returned as an array.
8485      * @param {Boolean} asString
8486      * @return {Object}
8487      */
8488     getValues : function(asString){
8489         //if (this.childForms) {
8490             // copy values from the child forms
8491         //    Roo.each(this.childForms, function (f) {
8492         //        this.setValues(f.getValues());
8493         //    }, this);
8494         //}
8495
8496
8497
8498         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8499         if(asString === true){
8500             return fs;
8501         }
8502         return Roo.urlDecode(fs);
8503     },
8504
8505     /**
8506      * Returns the fields in this form as an object with key/value pairs.
8507      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8508      * @return {Object}
8509      */
8510     getFieldValues : function(with_hidden)
8511     {
8512         var items = this.getItems();
8513         var ret = {};
8514         items.each(function(f){
8515             
8516             if (!f.getName()) {
8517                 return;
8518             }
8519             
8520             var v = f.getValue();
8521             
8522             if (f.inputType =='radio') {
8523                 if (typeof(ret[f.getName()]) == 'undefined') {
8524                     ret[f.getName()] = ''; // empty..
8525                 }
8526
8527                 if (!f.el.dom.checked) {
8528                     return;
8529
8530                 }
8531                 v = f.el.dom.value;
8532
8533             }
8534             
8535             if(f.xtype == 'MoneyField'){
8536                 ret[f.currencyName] = f.getCurrency();
8537             }
8538
8539             // not sure if this supported any more..
8540             if ((typeof(v) == 'object') && f.getRawValue) {
8541                 v = f.getRawValue() ; // dates..
8542             }
8543             // combo boxes where name != hiddenName...
8544             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8545                 ret[f.name] = f.getRawValue();
8546             }
8547             ret[f.getName()] = v;
8548         });
8549
8550         return ret;
8551     },
8552
8553     /**
8554      * Clears all invalid messages in this form.
8555      * @return {BasicForm} this
8556      */
8557     clearInvalid : function(){
8558         var items = this.getItems();
8559
8560         items.each(function(f){
8561            f.clearInvalid();
8562         });
8563
8564         return this;
8565     },
8566
8567     /**
8568      * Resets this form.
8569      * @return {BasicForm} this
8570      */
8571     reset : function(){
8572         var items = this.getItems();
8573         items.each(function(f){
8574             f.reset();
8575         });
8576
8577         Roo.each(this.childForms || [], function (f) {
8578             f.reset();
8579         });
8580
8581
8582         return this;
8583     },
8584     
8585     getItems : function()
8586     {
8587         var r=new Roo.util.MixedCollection(false, function(o){
8588             return o.id || (o.id = Roo.id());
8589         });
8590         var iter = function(el) {
8591             if (el.inputEl) {
8592                 r.add(el);
8593             }
8594             if (!el.items) {
8595                 return;
8596             }
8597             Roo.each(el.items,function(e) {
8598                 iter(e);
8599             });
8600         };
8601
8602         iter(this);
8603         return r;
8604     },
8605     
8606     hideFields : function(items)
8607     {
8608         Roo.each(items, function(i){
8609             
8610             var f = this.findField(i);
8611             
8612             if(!f){
8613                 return;
8614             }
8615             
8616             f.hide();
8617             
8618         }, this);
8619     },
8620     
8621     showFields : function(items)
8622     {
8623         Roo.each(items, function(i){
8624             
8625             var f = this.findField(i);
8626             
8627             if(!f){
8628                 return;
8629             }
8630             
8631             f.show();
8632             
8633         }, this);
8634     }
8635
8636 });
8637
8638 Roo.apply(Roo.bootstrap.Form, {
8639     
8640     popover : {
8641         
8642         padding : 5,
8643         
8644         isApplied : false,
8645         
8646         isMasked : false,
8647         
8648         form : false,
8649         
8650         target : false,
8651         
8652         toolTip : false,
8653         
8654         intervalID : false,
8655         
8656         maskEl : false,
8657         
8658         apply : function()
8659         {
8660             if(this.isApplied){
8661                 return;
8662             }
8663             
8664             this.maskEl = {
8665                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8666                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8667                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8668                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8669             };
8670             
8671             this.maskEl.top.enableDisplayMode("block");
8672             this.maskEl.left.enableDisplayMode("block");
8673             this.maskEl.bottom.enableDisplayMode("block");
8674             this.maskEl.right.enableDisplayMode("block");
8675             
8676             this.toolTip = new Roo.bootstrap.Tooltip({
8677                 cls : 'roo-form-error-popover',
8678                 alignment : {
8679                     'left' : ['r-l', [-2,0], 'right'],
8680                     'right' : ['l-r', [2,0], 'left'],
8681                     'bottom' : ['tl-bl', [0,2], 'top'],
8682                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8683                 }
8684             });
8685             
8686             this.toolTip.render(Roo.get(document.body));
8687
8688             this.toolTip.el.enableDisplayMode("block");
8689             
8690             Roo.get(document.body).on('click', function(){
8691                 this.unmask();
8692             }, this);
8693             
8694             Roo.get(document.body).on('touchstart', function(){
8695                 this.unmask();
8696             }, this);
8697             
8698             this.isApplied = true
8699         },
8700         
8701         mask : function(form, target)
8702         {
8703             this.form = form;
8704             
8705             this.target = target;
8706             
8707             if(!this.form.errorMask || !target.el){
8708                 return;
8709             }
8710             
8711             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8712             
8713             Roo.log(scrollable);
8714             
8715             var ot = this.target.el.calcOffsetsTo(scrollable);
8716             
8717             var scrollTo = ot[1] - this.form.maskOffset;
8718             
8719             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8720             
8721             scrollable.scrollTo('top', scrollTo);
8722             
8723             var box = this.target.el.getBox();
8724             Roo.log(box);
8725             var zIndex = Roo.bootstrap.Modal.zIndex++;
8726
8727             
8728             this.maskEl.top.setStyle('position', 'absolute');
8729             this.maskEl.top.setStyle('z-index', zIndex);
8730             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8731             this.maskEl.top.setLeft(0);
8732             this.maskEl.top.setTop(0);
8733             this.maskEl.top.show();
8734             
8735             this.maskEl.left.setStyle('position', 'absolute');
8736             this.maskEl.left.setStyle('z-index', zIndex);
8737             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8738             this.maskEl.left.setLeft(0);
8739             this.maskEl.left.setTop(box.y - this.padding);
8740             this.maskEl.left.show();
8741
8742             this.maskEl.bottom.setStyle('position', 'absolute');
8743             this.maskEl.bottom.setStyle('z-index', zIndex);
8744             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8745             this.maskEl.bottom.setLeft(0);
8746             this.maskEl.bottom.setTop(box.bottom + this.padding);
8747             this.maskEl.bottom.show();
8748
8749             this.maskEl.right.setStyle('position', 'absolute');
8750             this.maskEl.right.setStyle('z-index', zIndex);
8751             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8752             this.maskEl.right.setLeft(box.right + this.padding);
8753             this.maskEl.right.setTop(box.y - this.padding);
8754             this.maskEl.right.show();
8755
8756             this.toolTip.bindEl = this.target.el;
8757
8758             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8759
8760             var tip = this.target.blankText;
8761
8762             if(this.target.getValue() !== '' ) {
8763                 
8764                 if (this.target.invalidText.length) {
8765                     tip = this.target.invalidText;
8766                 } else if (this.target.regexText.length){
8767                     tip = this.target.regexText;
8768                 }
8769             }
8770
8771             this.toolTip.show(tip);
8772
8773             this.intervalID = window.setInterval(function() {
8774                 Roo.bootstrap.Form.popover.unmask();
8775             }, 10000);
8776
8777             window.onwheel = function(){ return false;};
8778             
8779             (function(){ this.isMasked = true; }).defer(500, this);
8780             
8781         },
8782         
8783         unmask : function()
8784         {
8785             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8786                 return;
8787             }
8788             
8789             this.maskEl.top.setStyle('position', 'absolute');
8790             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8791             this.maskEl.top.hide();
8792
8793             this.maskEl.left.setStyle('position', 'absolute');
8794             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8795             this.maskEl.left.hide();
8796
8797             this.maskEl.bottom.setStyle('position', 'absolute');
8798             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8799             this.maskEl.bottom.hide();
8800
8801             this.maskEl.right.setStyle('position', 'absolute');
8802             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8803             this.maskEl.right.hide();
8804             
8805             this.toolTip.hide();
8806             
8807             this.toolTip.el.hide();
8808             
8809             window.onwheel = function(){ return true;};
8810             
8811             if(this.intervalID){
8812                 window.clearInterval(this.intervalID);
8813                 this.intervalID = false;
8814             }
8815             
8816             this.isMasked = false;
8817             
8818         }
8819         
8820     }
8821     
8822 });
8823
8824 /*
8825  * Based on:
8826  * Ext JS Library 1.1.1
8827  * Copyright(c) 2006-2007, Ext JS, LLC.
8828  *
8829  * Originally Released Under LGPL - original licence link has changed is not relivant.
8830  *
8831  * Fork - LGPL
8832  * <script type="text/javascript">
8833  */
8834 /**
8835  * @class Roo.form.VTypes
8836  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8837  * @singleton
8838  */
8839 Roo.form.VTypes = function(){
8840     // closure these in so they are only created once.
8841     var alpha = /^[a-zA-Z_]+$/;
8842     var alphanum = /^[a-zA-Z0-9_]+$/;
8843     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8844     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8845
8846     // All these messages and functions are configurable
8847     return {
8848         /**
8849          * The function used to validate email addresses
8850          * @param {String} value The email address
8851          */
8852         'email' : function(v){
8853             return email.test(v);
8854         },
8855         /**
8856          * The error text to display when the email validation function returns false
8857          * @type String
8858          */
8859         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8860         /**
8861          * The keystroke filter mask to be applied on email input
8862          * @type RegExp
8863          */
8864         'emailMask' : /[a-z0-9_\.\-@]/i,
8865
8866         /**
8867          * The function used to validate URLs
8868          * @param {String} value The URL
8869          */
8870         'url' : function(v){
8871             return url.test(v);
8872         },
8873         /**
8874          * The error text to display when the url validation function returns false
8875          * @type String
8876          */
8877         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8878         
8879         /**
8880          * The function used to validate alpha values
8881          * @param {String} value The value
8882          */
8883         'alpha' : function(v){
8884             return alpha.test(v);
8885         },
8886         /**
8887          * The error text to display when the alpha validation function returns false
8888          * @type String
8889          */
8890         'alphaText' : 'This field should only contain letters and _',
8891         /**
8892          * The keystroke filter mask to be applied on alpha input
8893          * @type RegExp
8894          */
8895         'alphaMask' : /[a-z_]/i,
8896
8897         /**
8898          * The function used to validate alphanumeric values
8899          * @param {String} value The value
8900          */
8901         'alphanum' : function(v){
8902             return alphanum.test(v);
8903         },
8904         /**
8905          * The error text to display when the alphanumeric validation function returns false
8906          * @type String
8907          */
8908         'alphanumText' : 'This field should only contain letters, numbers and _',
8909         /**
8910          * The keystroke filter mask to be applied on alphanumeric input
8911          * @type RegExp
8912          */
8913         'alphanumMask' : /[a-z0-9_]/i
8914     };
8915 }();/*
8916  * - LGPL
8917  *
8918  * Input
8919  * 
8920  */
8921
8922 /**
8923  * @class Roo.bootstrap.Input
8924  * @extends Roo.bootstrap.Component
8925  * Bootstrap Input class
8926  * @cfg {Boolean} disabled is it disabled
8927  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8928  * @cfg {String} name name of the input
8929  * @cfg {string} fieldLabel - the label associated
8930  * @cfg {string} placeholder - placeholder to put in text.
8931  * @cfg {string}  before - input group add on before
8932  * @cfg {string} after - input group add on after
8933  * @cfg {string} size - (lg|sm) or leave empty..
8934  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8935  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8936  * @cfg {Number} md colspan out of 12 for computer-sized screens
8937  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8938  * @cfg {string} value default value of the input
8939  * @cfg {Number} labelWidth set the width of label 
8940  * @cfg {Number} labellg set the width of label (1-12)
8941  * @cfg {Number} labelmd set the width of label (1-12)
8942  * @cfg {Number} labelsm set the width of label (1-12)
8943  * @cfg {Number} labelxs set the width of label (1-12)
8944  * @cfg {String} labelAlign (top|left)
8945  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8946  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8947  * @cfg {String} indicatorpos (left|right) default left
8948  * @cfg {String} capture (user|camera) use for file input only. (default empty)
8949  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
8950
8951  * @cfg {String} align (left|center|right) Default left
8952  * @cfg {Boolean} forceFeedback (true|false) Default false
8953  * 
8954  * @constructor
8955  * Create a new Input
8956  * @param {Object} config The config object
8957  */
8958
8959 Roo.bootstrap.Input = function(config){
8960     
8961     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8962     
8963     this.addEvents({
8964         /**
8965          * @event focus
8966          * Fires when this field receives input focus.
8967          * @param {Roo.form.Field} this
8968          */
8969         focus : true,
8970         /**
8971          * @event blur
8972          * Fires when this field loses input focus.
8973          * @param {Roo.form.Field} this
8974          */
8975         blur : true,
8976         /**
8977          * @event specialkey
8978          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8979          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8980          * @param {Roo.form.Field} this
8981          * @param {Roo.EventObject} e The event object
8982          */
8983         specialkey : true,
8984         /**
8985          * @event change
8986          * Fires just before the field blurs if the field value has changed.
8987          * @param {Roo.form.Field} this
8988          * @param {Mixed} newValue The new value
8989          * @param {Mixed} oldValue The original value
8990          */
8991         change : true,
8992         /**
8993          * @event invalid
8994          * Fires after the field has been marked as invalid.
8995          * @param {Roo.form.Field} this
8996          * @param {String} msg The validation message
8997          */
8998         invalid : true,
8999         /**
9000          * @event valid
9001          * Fires after the field has been validated with no errors.
9002          * @param {Roo.form.Field} this
9003          */
9004         valid : true,
9005          /**
9006          * @event keyup
9007          * Fires after the key up
9008          * @param {Roo.form.Field} this
9009          * @param {Roo.EventObject}  e The event Object
9010          */
9011         keyup : true
9012     });
9013 };
9014
9015 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
9016      /**
9017      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
9018       automatic validation (defaults to "keyup").
9019      */
9020     validationEvent : "keyup",
9021      /**
9022      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
9023      */
9024     validateOnBlur : true,
9025     /**
9026      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
9027      */
9028     validationDelay : 250,
9029      /**
9030      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
9031      */
9032     focusClass : "x-form-focus",  // not needed???
9033     
9034        
9035     /**
9036      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9037      */
9038     invalidClass : "has-warning",
9039     
9040     /**
9041      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
9042      */
9043     validClass : "has-success",
9044     
9045     /**
9046      * @cfg {Boolean} hasFeedback (true|false) default true
9047      */
9048     hasFeedback : true,
9049     
9050     /**
9051      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9052      */
9053     invalidFeedbackClass : "glyphicon-warning-sign",
9054     
9055     /**
9056      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
9057      */
9058     validFeedbackClass : "glyphicon-ok",
9059     
9060     /**
9061      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
9062      */
9063     selectOnFocus : false,
9064     
9065      /**
9066      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
9067      */
9068     maskRe : null,
9069        /**
9070      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
9071      */
9072     vtype : null,
9073     
9074       /**
9075      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
9076      */
9077     disableKeyFilter : false,
9078     
9079        /**
9080      * @cfg {Boolean} disabled True to disable the field (defaults to false).
9081      */
9082     disabled : false,
9083      /**
9084      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
9085      */
9086     allowBlank : true,
9087     /**
9088      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
9089      */
9090     blankText : "Please complete this mandatory field",
9091     
9092      /**
9093      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
9094      */
9095     minLength : 0,
9096     /**
9097      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
9098      */
9099     maxLength : Number.MAX_VALUE,
9100     /**
9101      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
9102      */
9103     minLengthText : "The minimum length for this field is {0}",
9104     /**
9105      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
9106      */
9107     maxLengthText : "The maximum length for this field is {0}",
9108   
9109     
9110     /**
9111      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
9112      * If available, this function will be called only after the basic validators all return true, and will be passed the
9113      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
9114      */
9115     validator : null,
9116     /**
9117      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
9118      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
9119      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
9120      */
9121     regex : null,
9122     /**
9123      * @cfg {String} regexText -- Depricated - use Invalid Text
9124      */
9125     regexText : "",
9126     
9127     /**
9128      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
9129      */
9130     invalidText : "",
9131     
9132     
9133     
9134     autocomplete: false,
9135     
9136     
9137     fieldLabel : '',
9138     inputType : 'text',
9139     
9140     name : false,
9141     placeholder: false,
9142     before : false,
9143     after : false,
9144     size : false,
9145     hasFocus : false,
9146     preventMark: false,
9147     isFormField : true,
9148     value : '',
9149     labelWidth : 2,
9150     labelAlign : false,
9151     readOnly : false,
9152     align : false,
9153     formatedValue : false,
9154     forceFeedback : false,
9155     
9156     indicatorpos : 'left',
9157     
9158     labellg : 0,
9159     labelmd : 0,
9160     labelsm : 0,
9161     labelxs : 0,
9162     
9163     capture : '',
9164     accept : '',
9165     
9166     parentLabelAlign : function()
9167     {
9168         var parent = this;
9169         while (parent.parent()) {
9170             parent = parent.parent();
9171             if (typeof(parent.labelAlign) !='undefined') {
9172                 return parent.labelAlign;
9173             }
9174         }
9175         return 'left';
9176         
9177     },
9178     
9179     getAutoCreate : function()
9180     {
9181         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9182         
9183         var id = Roo.id();
9184         
9185         var cfg = {};
9186         
9187         if(this.inputType != 'hidden'){
9188             cfg.cls = 'form-group' //input-group
9189         }
9190         
9191         var input =  {
9192             tag: 'input',
9193             id : id,
9194             type : this.inputType,
9195             value : this.value,
9196             cls : 'form-control',
9197             placeholder : this.placeholder || '',
9198             autocomplete : this.autocomplete || 'new-password'
9199         };
9200         
9201         if(this.capture.length){
9202             input.capture = this.capture;
9203         }
9204         
9205         if(this.accept.length){
9206             input.accept = this.accept + "/*";
9207         }
9208         
9209         if(this.align){
9210             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
9211         }
9212         
9213         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9214             input.maxLength = this.maxLength;
9215         }
9216         
9217         if (this.disabled) {
9218             input.disabled=true;
9219         }
9220         
9221         if (this.readOnly) {
9222             input.readonly=true;
9223         }
9224         
9225         if (this.name) {
9226             input.name = this.name;
9227         }
9228         
9229         if (this.size) {
9230             input.cls += ' input-' + this.size;
9231         }
9232         
9233         var settings=this;
9234         ['xs','sm','md','lg'].map(function(size){
9235             if (settings[size]) {
9236                 cfg.cls += ' col-' + size + '-' + settings[size];
9237             }
9238         });
9239         
9240         var inputblock = input;
9241         
9242         var feedback = {
9243             tag: 'span',
9244             cls: 'glyphicon form-control-feedback'
9245         };
9246             
9247         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9248             
9249             inputblock = {
9250                 cls : 'has-feedback',
9251                 cn :  [
9252                     input,
9253                     feedback
9254                 ] 
9255             };  
9256         }
9257         
9258         if (this.before || this.after) {
9259             
9260             inputblock = {
9261                 cls : 'input-group',
9262                 cn :  [] 
9263             };
9264             
9265             if (this.before && typeof(this.before) == 'string') {
9266                 
9267                 inputblock.cn.push({
9268                     tag :'span',
9269                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
9270                     html : this.before
9271                 });
9272             }
9273             if (this.before && typeof(this.before) == 'object') {
9274                 this.before = Roo.factory(this.before);
9275                 
9276                 inputblock.cn.push({
9277                     tag :'span',
9278                     cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
9279                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9280                 });
9281             }
9282             
9283             inputblock.cn.push(input);
9284             
9285             if (this.after && typeof(this.after) == 'string') {
9286                 inputblock.cn.push({
9287                     tag :'span',
9288                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
9289                     html : this.after
9290                 });
9291             }
9292             if (this.after && typeof(this.after) == 'object') {
9293                 this.after = Roo.factory(this.after);
9294                 
9295                 inputblock.cn.push({
9296                     tag :'span',
9297                     cls : 'roo-input-after input-group-append input-group-text input-group-' +
9298                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
9299                 });
9300             }
9301             
9302             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9303                 inputblock.cls += ' has-feedback';
9304                 inputblock.cn.push(feedback);
9305             }
9306         };
9307         var indicator = {
9308             tag : 'i',
9309             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
9310             tooltip : 'This field is required'
9311         };
9312         if (Roo.bootstrap.version == 4) {
9313             indicator = {
9314                 tag : 'i',
9315                 style : 'display-none'
9316             };
9317         }
9318         if (align ==='left' && this.fieldLabel.length) {
9319             
9320             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
9321             
9322             cfg.cn = [
9323                 indicator,
9324                 {
9325                     tag: 'label',
9326                     'for' :  id,
9327                     cls : 'control-label col-form-label',
9328                     html : this.fieldLabel
9329
9330                 },
9331                 {
9332                     cls : "", 
9333                     cn: [
9334                         inputblock
9335                     ]
9336                 }
9337             ];
9338             
9339             var labelCfg = cfg.cn[1];
9340             var contentCfg = cfg.cn[2];
9341             
9342             if(this.indicatorpos == 'right'){
9343                 cfg.cn = [
9344                     {
9345                         tag: 'label',
9346                         'for' :  id,
9347                         cls : 'control-label col-form-label',
9348                         cn : [
9349                             {
9350                                 tag : 'span',
9351                                 html : this.fieldLabel
9352                             },
9353                             indicator
9354                         ]
9355                     },
9356                     {
9357                         cls : "",
9358                         cn: [
9359                             inputblock
9360                         ]
9361                     }
9362
9363                 ];
9364                 
9365                 labelCfg = cfg.cn[0];
9366                 contentCfg = cfg.cn[1];
9367             
9368             }
9369             
9370             if(this.labelWidth > 12){
9371                 labelCfg.style = "width: " + this.labelWidth + 'px';
9372             }
9373             
9374             if(this.labelWidth < 13 && this.labelmd == 0){
9375                 this.labelmd = this.labelWidth;
9376             }
9377             
9378             if(this.labellg > 0){
9379                 labelCfg.cls += ' col-lg-' + this.labellg;
9380                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9381             }
9382             
9383             if(this.labelmd > 0){
9384                 labelCfg.cls += ' col-md-' + this.labelmd;
9385                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
9386             }
9387             
9388             if(this.labelsm > 0){
9389                 labelCfg.cls += ' col-sm-' + this.labelsm;
9390                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
9391             }
9392             
9393             if(this.labelxs > 0){
9394                 labelCfg.cls += ' col-xs-' + this.labelxs;
9395                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
9396             }
9397             
9398             
9399         } else if ( this.fieldLabel.length) {
9400                 
9401             cfg.cn = [
9402                 {
9403                     tag : 'i',
9404                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9405                     tooltip : 'This field is required'
9406                 },
9407                 {
9408                     tag: 'label',
9409                    //cls : 'input-group-addon',
9410                     html : this.fieldLabel
9411
9412                 },
9413
9414                inputblock
9415
9416            ];
9417            
9418            if(this.indicatorpos == 'right'){
9419                 
9420                 cfg.cn = [
9421                     {
9422                         tag: 'label',
9423                        //cls : 'input-group-addon',
9424                         html : this.fieldLabel
9425
9426                     },
9427                     {
9428                         tag : 'i',
9429                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9430                         tooltip : 'This field is required'
9431                     },
9432
9433                    inputblock
9434
9435                ];
9436
9437             }
9438
9439         } else {
9440             
9441             cfg.cn = [
9442
9443                     inputblock
9444
9445             ];
9446                 
9447                 
9448         };
9449         
9450         if (this.parentType === 'Navbar' &&  this.parent().bar) {
9451            cfg.cls += ' navbar-form';
9452         }
9453         
9454         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
9455             // on BS4 we do this only if not form 
9456             cfg.cls += ' navbar-form';
9457             cfg.tag = 'li';
9458         }
9459         
9460         return cfg;
9461         
9462     },
9463     /**
9464      * return the real input element.
9465      */
9466     inputEl: function ()
9467     {
9468         return this.el.select('input.form-control',true).first();
9469     },
9470     
9471     tooltipEl : function()
9472     {
9473         return this.inputEl();
9474     },
9475     
9476     indicatorEl : function()
9477     {
9478         if (Roo.bootstrap.version == 4) {
9479             return false; // not enabled in v4 yet.
9480         }
9481         
9482         var indicator = this.el.select('i.roo-required-indicator',true).first();
9483         
9484         if(!indicator){
9485             return false;
9486         }
9487         
9488         return indicator;
9489         
9490     },
9491     
9492     setDisabled : function(v)
9493     {
9494         var i  = this.inputEl().dom;
9495         if (!v) {
9496             i.removeAttribute('disabled');
9497             return;
9498             
9499         }
9500         i.setAttribute('disabled','true');
9501     },
9502     initEvents : function()
9503     {
9504           
9505         this.inputEl().on("keydown" , this.fireKey,  this);
9506         this.inputEl().on("focus", this.onFocus,  this);
9507         this.inputEl().on("blur", this.onBlur,  this);
9508         
9509         this.inputEl().relayEvent('keyup', this);
9510         
9511         this.indicator = this.indicatorEl();
9512         
9513         if(this.indicator){
9514             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
9515         }
9516  
9517         // reference to original value for reset
9518         this.originalValue = this.getValue();
9519         //Roo.form.TextField.superclass.initEvents.call(this);
9520         if(this.validationEvent == 'keyup'){
9521             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
9522             this.inputEl().on('keyup', this.filterValidation, this);
9523         }
9524         else if(this.validationEvent !== false){
9525             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
9526         }
9527         
9528         if(this.selectOnFocus){
9529             this.on("focus", this.preFocus, this);
9530             
9531         }
9532         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
9533             this.inputEl().on("keypress", this.filterKeys, this);
9534         } else {
9535             this.inputEl().relayEvent('keypress', this);
9536         }
9537        /* if(this.grow){
9538             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9539             this.el.on("click", this.autoSize,  this);
9540         }
9541         */
9542         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9543             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9544         }
9545         
9546         if (typeof(this.before) == 'object') {
9547             this.before.render(this.el.select('.roo-input-before',true).first());
9548         }
9549         if (typeof(this.after) == 'object') {
9550             this.after.render(this.el.select('.roo-input-after',true).first());
9551         }
9552         
9553         this.inputEl().on('change', this.onChange, this);
9554         
9555     },
9556     filterValidation : function(e){
9557         if(!e.isNavKeyPress()){
9558             this.validationTask.delay(this.validationDelay);
9559         }
9560     },
9561      /**
9562      * Validates the field value
9563      * @return {Boolean} True if the value is valid, else false
9564      */
9565     validate : function(){
9566         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9567         if(this.disabled || this.validateValue(this.getRawValue())){
9568             this.markValid();
9569             return true;
9570         }
9571         
9572         this.markInvalid();
9573         return false;
9574     },
9575     
9576     
9577     /**
9578      * Validates a value according to the field's validation rules and marks the field as invalid
9579      * if the validation fails
9580      * @param {Mixed} value The value to validate
9581      * @return {Boolean} True if the value is valid, else false
9582      */
9583     validateValue : function(value)
9584     {
9585         if(this.getVisibilityEl().hasClass('hidden')){
9586             return true;
9587         }
9588         
9589         if(value.length < 1)  { // if it's blank
9590             if(this.allowBlank){
9591                 return true;
9592             }
9593             return false;
9594         }
9595         
9596         if(value.length < this.minLength){
9597             return false;
9598         }
9599         if(value.length > this.maxLength){
9600             return false;
9601         }
9602         if(this.vtype){
9603             var vt = Roo.form.VTypes;
9604             if(!vt[this.vtype](value, this)){
9605                 return false;
9606             }
9607         }
9608         if(typeof this.validator == "function"){
9609             var msg = this.validator(value);
9610             if(msg !== true){
9611                 return false;
9612             }
9613             if (typeof(msg) == 'string') {
9614                 this.invalidText = msg;
9615             }
9616         }
9617         
9618         if(this.regex && !this.regex.test(value)){
9619             return false;
9620         }
9621         
9622         return true;
9623     },
9624     
9625      // private
9626     fireKey : function(e){
9627         //Roo.log('field ' + e.getKey());
9628         if(e.isNavKeyPress()){
9629             this.fireEvent("specialkey", this, e);
9630         }
9631     },
9632     focus : function (selectText){
9633         if(this.rendered){
9634             this.inputEl().focus();
9635             if(selectText === true){
9636                 this.inputEl().dom.select();
9637             }
9638         }
9639         return this;
9640     } ,
9641     
9642     onFocus : function(){
9643         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9644            // this.el.addClass(this.focusClass);
9645         }
9646         if(!this.hasFocus){
9647             this.hasFocus = true;
9648             this.startValue = this.getValue();
9649             this.fireEvent("focus", this);
9650         }
9651     },
9652     
9653     beforeBlur : Roo.emptyFn,
9654
9655     
9656     // private
9657     onBlur : function(){
9658         this.beforeBlur();
9659         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9660             //this.el.removeClass(this.focusClass);
9661         }
9662         this.hasFocus = false;
9663         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9664             this.validate();
9665         }
9666         var v = this.getValue();
9667         if(String(v) !== String(this.startValue)){
9668             this.fireEvent('change', this, v, this.startValue);
9669         }
9670         this.fireEvent("blur", this);
9671     },
9672     
9673     onChange : function(e)
9674     {
9675         var v = this.getValue();
9676         if(String(v) !== String(this.startValue)){
9677             this.fireEvent('change', this, v, this.startValue);
9678         }
9679         
9680     },
9681     
9682     /**
9683      * Resets the current field value to the originally loaded value and clears any validation messages
9684      */
9685     reset : function(){
9686         this.setValue(this.originalValue);
9687         this.validate();
9688     },
9689      /**
9690      * Returns the name of the field
9691      * @return {Mixed} name The name field
9692      */
9693     getName: function(){
9694         return this.name;
9695     },
9696      /**
9697      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9698      * @return {Mixed} value The field value
9699      */
9700     getValue : function(){
9701         
9702         var v = this.inputEl().getValue();
9703         
9704         return v;
9705     },
9706     /**
9707      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9708      * @return {Mixed} value The field value
9709      */
9710     getRawValue : function(){
9711         var v = this.inputEl().getValue();
9712         
9713         return v;
9714     },
9715     
9716     /**
9717      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9718      * @param {Mixed} value The value to set
9719      */
9720     setRawValue : function(v){
9721         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9722     },
9723     
9724     selectText : function(start, end){
9725         var v = this.getRawValue();
9726         if(v.length > 0){
9727             start = start === undefined ? 0 : start;
9728             end = end === undefined ? v.length : end;
9729             var d = this.inputEl().dom;
9730             if(d.setSelectionRange){
9731                 d.setSelectionRange(start, end);
9732             }else if(d.createTextRange){
9733                 var range = d.createTextRange();
9734                 range.moveStart("character", start);
9735                 range.moveEnd("character", v.length-end);
9736                 range.select();
9737             }
9738         }
9739     },
9740     
9741     /**
9742      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9743      * @param {Mixed} value The value to set
9744      */
9745     setValue : function(v){
9746         this.value = v;
9747         if(this.rendered){
9748             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9749             this.validate();
9750         }
9751     },
9752     
9753     /*
9754     processValue : function(value){
9755         if(this.stripCharsRe){
9756             var newValue = value.replace(this.stripCharsRe, '');
9757             if(newValue !== value){
9758                 this.setRawValue(newValue);
9759                 return newValue;
9760             }
9761         }
9762         return value;
9763     },
9764   */
9765     preFocus : function(){
9766         
9767         if(this.selectOnFocus){
9768             this.inputEl().dom.select();
9769         }
9770     },
9771     filterKeys : function(e){
9772         var k = e.getKey();
9773         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9774             return;
9775         }
9776         var c = e.getCharCode(), cc = String.fromCharCode(c);
9777         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9778             return;
9779         }
9780         if(!this.maskRe.test(cc)){
9781             e.stopEvent();
9782         }
9783     },
9784      /**
9785      * Clear any invalid styles/messages for this field
9786      */
9787     clearInvalid : function(){
9788         
9789         if(!this.el || this.preventMark){ // not rendered
9790             return;
9791         }
9792         
9793         
9794         this.el.removeClass([this.invalidClass, 'is-invalid']);
9795         
9796         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9797             
9798             var feedback = this.el.select('.form-control-feedback', true).first();
9799             
9800             if(feedback){
9801                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9802             }
9803             
9804         }
9805         
9806         if(this.indicator){
9807             this.indicator.removeClass('visible');
9808             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9809         }
9810         
9811         this.fireEvent('valid', this);
9812     },
9813     
9814      /**
9815      * Mark this field as valid
9816      */
9817     markValid : function()
9818     {
9819         if(!this.el  || this.preventMark){ // not rendered...
9820             return;
9821         }
9822         
9823         this.el.removeClass([this.invalidClass, this.validClass]);
9824         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9825
9826         var feedback = this.el.select('.form-control-feedback', true).first();
9827             
9828         if(feedback){
9829             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9830         }
9831         
9832         if(this.indicator){
9833             this.indicator.removeClass('visible');
9834             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9835         }
9836         
9837         if(this.disabled){
9838             return;
9839         }
9840         
9841         if(this.allowBlank && !this.getRawValue().length){
9842             return;
9843         }
9844         if (Roo.bootstrap.version == 3) {
9845             this.el.addClass(this.validClass);
9846         } else {
9847             this.inputEl().addClass('is-valid');
9848         }
9849
9850         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9851             
9852             var feedback = this.el.select('.form-control-feedback', true).first();
9853             
9854             if(feedback){
9855                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9856                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9857             }
9858             
9859         }
9860         
9861         this.fireEvent('valid', this);
9862     },
9863     
9864      /**
9865      * Mark this field as invalid
9866      * @param {String} msg The validation message
9867      */
9868     markInvalid : function(msg)
9869     {
9870         if(!this.el  || this.preventMark){ // not rendered
9871             return;
9872         }
9873         
9874         this.el.removeClass([this.invalidClass, this.validClass]);
9875         this.inputEl().removeClass(['is-valid', 'is-invalid']);
9876         
9877         var feedback = this.el.select('.form-control-feedback', true).first();
9878             
9879         if(feedback){
9880             this.el.select('.form-control-feedback', true).first().removeClass(
9881                     [this.invalidFeedbackClass, this.validFeedbackClass]);
9882         }
9883
9884         if(this.disabled){
9885             return;
9886         }
9887         
9888         if(this.allowBlank && !this.getRawValue().length){
9889             return;
9890         }
9891         
9892         if(this.indicator){
9893             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
9894             this.indicator.addClass('visible');
9895         }
9896         if (Roo.bootstrap.version == 3) {
9897             this.el.addClass(this.invalidClass);
9898         } else {
9899             this.inputEl().addClass('is-invalid');
9900         }
9901         
9902         
9903         
9904         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9905             
9906             var feedback = this.el.select('.form-control-feedback', true).first();
9907             
9908             if(feedback){
9909                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9910                 
9911                 if(this.getValue().length || this.forceFeedback){
9912                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9913                 }
9914                 
9915             }
9916             
9917         }
9918         
9919         this.fireEvent('invalid', this, msg);
9920     },
9921     // private
9922     SafariOnKeyDown : function(event)
9923     {
9924         // this is a workaround for a password hang bug on chrome/ webkit.
9925         if (this.inputEl().dom.type != 'password') {
9926             return;
9927         }
9928         
9929         var isSelectAll = false;
9930         
9931         if(this.inputEl().dom.selectionEnd > 0){
9932             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9933         }
9934         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9935             event.preventDefault();
9936             this.setValue('');
9937             return;
9938         }
9939         
9940         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9941             
9942             event.preventDefault();
9943             // this is very hacky as keydown always get's upper case.
9944             //
9945             var cc = String.fromCharCode(event.getCharCode());
9946             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9947             
9948         }
9949     },
9950     adjustWidth : function(tag, w){
9951         tag = tag.toLowerCase();
9952         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9953             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9954                 if(tag == 'input'){
9955                     return w + 2;
9956                 }
9957                 if(tag == 'textarea'){
9958                     return w-2;
9959                 }
9960             }else if(Roo.isOpera){
9961                 if(tag == 'input'){
9962                     return w + 2;
9963                 }
9964                 if(tag == 'textarea'){
9965                     return w-2;
9966                 }
9967             }
9968         }
9969         return w;
9970     },
9971     
9972     setFieldLabel : function(v)
9973     {
9974         if(!this.rendered){
9975             return;
9976         }
9977         
9978         if(this.indicatorEl()){
9979             var ar = this.el.select('label > span',true);
9980             
9981             if (ar.elements.length) {
9982                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9983                 this.fieldLabel = v;
9984                 return;
9985             }
9986             
9987             var br = this.el.select('label',true);
9988             
9989             if(br.elements.length) {
9990                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9991                 this.fieldLabel = v;
9992                 return;
9993             }
9994             
9995             Roo.log('Cannot Found any of label > span || label in input');
9996             return;
9997         }
9998         
9999         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
10000         this.fieldLabel = v;
10001         
10002         
10003     }
10004 });
10005
10006  
10007 /*
10008  * - LGPL
10009  *
10010  * Input
10011  * 
10012  */
10013
10014 /**
10015  * @class Roo.bootstrap.TextArea
10016  * @extends Roo.bootstrap.Input
10017  * Bootstrap TextArea class
10018  * @cfg {Number} cols Specifies the visible width of a text area
10019  * @cfg {Number} rows Specifies the visible number of lines in a text area
10020  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
10021  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
10022  * @cfg {string} html text
10023  * 
10024  * @constructor
10025  * Create a new TextArea
10026  * @param {Object} config The config object
10027  */
10028
10029 Roo.bootstrap.TextArea = function(config){
10030     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
10031    
10032 };
10033
10034 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
10035      
10036     cols : false,
10037     rows : 5,
10038     readOnly : false,
10039     warp : 'soft',
10040     resize : false,
10041     value: false,
10042     html: false,
10043     
10044     getAutoCreate : function(){
10045         
10046         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10047         
10048         var id = Roo.id();
10049         
10050         var cfg = {};
10051         
10052         if(this.inputType != 'hidden'){
10053             cfg.cls = 'form-group' //input-group
10054         }
10055         
10056         var input =  {
10057             tag: 'textarea',
10058             id : id,
10059             warp : this.warp,
10060             rows : this.rows,
10061             value : this.value || '',
10062             html: this.html || '',
10063             cls : 'form-control',
10064             placeholder : this.placeholder || '' 
10065             
10066         };
10067         
10068         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10069             input.maxLength = this.maxLength;
10070         }
10071         
10072         if(this.resize){
10073             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
10074         }
10075         
10076         if(this.cols){
10077             input.cols = this.cols;
10078         }
10079         
10080         if (this.readOnly) {
10081             input.readonly = true;
10082         }
10083         
10084         if (this.name) {
10085             input.name = this.name;
10086         }
10087         
10088         if (this.size) {
10089             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
10090         }
10091         
10092         var settings=this;
10093         ['xs','sm','md','lg'].map(function(size){
10094             if (settings[size]) {
10095                 cfg.cls += ' col-' + size + '-' + settings[size];
10096             }
10097         });
10098         
10099         var inputblock = input;
10100         
10101         if(this.hasFeedback && !this.allowBlank){
10102             
10103             var feedback = {
10104                 tag: 'span',
10105                 cls: 'glyphicon form-control-feedback'
10106             };
10107
10108             inputblock = {
10109                 cls : 'has-feedback',
10110                 cn :  [
10111                     input,
10112                     feedback
10113                 ] 
10114             };  
10115         }
10116         
10117         
10118         if (this.before || this.after) {
10119             
10120             inputblock = {
10121                 cls : 'input-group',
10122                 cn :  [] 
10123             };
10124             if (this.before) {
10125                 inputblock.cn.push({
10126                     tag :'span',
10127                     cls : 'input-group-addon',
10128                     html : this.before
10129                 });
10130             }
10131             
10132             inputblock.cn.push(input);
10133             
10134             if(this.hasFeedback && !this.allowBlank){
10135                 inputblock.cls += ' has-feedback';
10136                 inputblock.cn.push(feedback);
10137             }
10138             
10139             if (this.after) {
10140                 inputblock.cn.push({
10141                     tag :'span',
10142                     cls : 'input-group-addon',
10143                     html : this.after
10144                 });
10145             }
10146             
10147         }
10148         
10149         if (align ==='left' && this.fieldLabel.length) {
10150             cfg.cn = [
10151                 {
10152                     tag: 'label',
10153                     'for' :  id,
10154                     cls : 'control-label',
10155                     html : this.fieldLabel
10156                 },
10157                 {
10158                     cls : "",
10159                     cn: [
10160                         inputblock
10161                     ]
10162                 }
10163
10164             ];
10165             
10166             if(this.labelWidth > 12){
10167                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
10168             }
10169
10170             if(this.labelWidth < 13 && this.labelmd == 0){
10171                 this.labelmd = this.labelWidth;
10172             }
10173
10174             if(this.labellg > 0){
10175                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
10176                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
10177             }
10178
10179             if(this.labelmd > 0){
10180                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
10181                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
10182             }
10183
10184             if(this.labelsm > 0){
10185                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
10186                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
10187             }
10188
10189             if(this.labelxs > 0){
10190                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
10191                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
10192             }
10193             
10194         } else if ( this.fieldLabel.length) {
10195             cfg.cn = [
10196
10197                {
10198                    tag: 'label',
10199                    //cls : 'input-group-addon',
10200                    html : this.fieldLabel
10201
10202                },
10203
10204                inputblock
10205
10206            ];
10207
10208         } else {
10209
10210             cfg.cn = [
10211
10212                 inputblock
10213
10214             ];
10215                 
10216         }
10217         
10218         if (this.disabled) {
10219             input.disabled=true;
10220         }
10221         
10222         return cfg;
10223         
10224     },
10225     /**
10226      * return the real textarea element.
10227      */
10228     inputEl: function ()
10229     {
10230         return this.el.select('textarea.form-control',true).first();
10231     },
10232     
10233     /**
10234      * Clear any invalid styles/messages for this field
10235      */
10236     clearInvalid : function()
10237     {
10238         
10239         if(!this.el || this.preventMark){ // not rendered
10240             return;
10241         }
10242         
10243         var label = this.el.select('label', true).first();
10244         var icon = this.el.select('i.fa-star', true).first();
10245         
10246         if(label && icon){
10247             icon.remove();
10248         }
10249         this.el.removeClass( this.validClass);
10250         this.inputEl().removeClass('is-invalid');
10251          
10252         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10253             
10254             var feedback = this.el.select('.form-control-feedback', true).first();
10255             
10256             if(feedback){
10257                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
10258             }
10259             
10260         }
10261         
10262         this.fireEvent('valid', this);
10263     },
10264     
10265      /**
10266      * Mark this field as valid
10267      */
10268     markValid : function()
10269     {
10270         if(!this.el  || this.preventMark){ // not rendered
10271             return;
10272         }
10273         
10274         this.el.removeClass([this.invalidClass, this.validClass]);
10275         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10276         
10277         var feedback = this.el.select('.form-control-feedback', true).first();
10278             
10279         if(feedback){
10280             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10281         }
10282
10283         if(this.disabled || this.allowBlank){
10284             return;
10285         }
10286         
10287         var label = this.el.select('label', true).first();
10288         var icon = this.el.select('i.fa-star', true).first();
10289         
10290         if(label && icon){
10291             icon.remove();
10292         }
10293         if (Roo.bootstrap.version == 3) {
10294             this.el.addClass(this.validClass);
10295         } else {
10296             this.inputEl().addClass('is-valid');
10297         }
10298         
10299         
10300         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
10301             
10302             var feedback = this.el.select('.form-control-feedback', true).first();
10303             
10304             if(feedback){
10305                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10306                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
10307             }
10308             
10309         }
10310         
10311         this.fireEvent('valid', this);
10312     },
10313     
10314      /**
10315      * Mark this field as invalid
10316      * @param {String} msg The validation message
10317      */
10318     markInvalid : function(msg)
10319     {
10320         if(!this.el  || this.preventMark){ // not rendered
10321             return;
10322         }
10323         
10324         this.el.removeClass([this.invalidClass, this.validClass]);
10325         this.inputEl().removeClass(['is-valid', 'is-invalid']);
10326         
10327         var feedback = this.el.select('.form-control-feedback', true).first();
10328             
10329         if(feedback){
10330             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10331         }
10332
10333         if(this.disabled || this.allowBlank){
10334             return;
10335         }
10336         
10337         var label = this.el.select('label', true).first();
10338         var icon = this.el.select('i.fa-star', true).first();
10339         
10340         if(!this.getValue().length && label && !icon){
10341             this.el.createChild({
10342                 tag : 'i',
10343                 cls : 'text-danger fa fa-lg fa-star',
10344                 tooltip : 'This field is required',
10345                 style : 'margin-right:5px;'
10346             }, label, true);
10347         }
10348         
10349         if (Roo.bootstrap.version == 3) {
10350             this.el.addClass(this.invalidClass);
10351         } else {
10352             this.inputEl().addClass('is-invalid');
10353         }
10354         
10355         // fixme ... this may be depricated need to test..
10356         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10357             
10358             var feedback = this.el.select('.form-control-feedback', true).first();
10359             
10360             if(feedback){
10361                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
10362                 
10363                 if(this.getValue().length || this.forceFeedback){
10364                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
10365                 }
10366                 
10367             }
10368             
10369         }
10370         
10371         this.fireEvent('invalid', this, msg);
10372     }
10373 });
10374
10375  
10376 /*
10377  * - LGPL
10378  *
10379  * trigger field - base class for combo..
10380  * 
10381  */
10382  
10383 /**
10384  * @class Roo.bootstrap.TriggerField
10385  * @extends Roo.bootstrap.Input
10386  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
10387  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
10388  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
10389  * for which you can provide a custom implementation.  For example:
10390  * <pre><code>
10391 var trigger = new Roo.bootstrap.TriggerField();
10392 trigger.onTriggerClick = myTriggerFn;
10393 trigger.applyTo('my-field');
10394 </code></pre>
10395  *
10396  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
10397  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
10398  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
10399  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
10400  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
10401
10402  * @constructor
10403  * Create a new TriggerField.
10404  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
10405  * to the base TextField)
10406  */
10407 Roo.bootstrap.TriggerField = function(config){
10408     this.mimicing = false;
10409     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
10410 };
10411
10412 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
10413     /**
10414      * @cfg {String} triggerClass A CSS class to apply to the trigger
10415      */
10416      /**
10417      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
10418      */
10419     hideTrigger:false,
10420
10421     /**
10422      * @cfg {Boolean} removable (true|false) special filter default false
10423      */
10424     removable : false,
10425     
10426     /** @cfg {Boolean} grow @hide */
10427     /** @cfg {Number} growMin @hide */
10428     /** @cfg {Number} growMax @hide */
10429
10430     /**
10431      * @hide 
10432      * @method
10433      */
10434     autoSize: Roo.emptyFn,
10435     // private
10436     monitorTab : true,
10437     // private
10438     deferHeight : true,
10439
10440     
10441     actionMode : 'wrap',
10442     
10443     caret : false,
10444     
10445     
10446     getAutoCreate : function(){
10447        
10448         var align = this.labelAlign || this.parentLabelAlign();
10449         
10450         var id = Roo.id();
10451         
10452         var cfg = {
10453             cls: 'form-group' //input-group
10454         };
10455         
10456         
10457         var input =  {
10458             tag: 'input',
10459             id : id,
10460             type : this.inputType,
10461             cls : 'form-control',
10462             autocomplete: 'new-password',
10463             placeholder : this.placeholder || '' 
10464             
10465         };
10466         if (this.name) {
10467             input.name = this.name;
10468         }
10469         if (this.size) {
10470             input.cls += ' input-' + this.size;
10471         }
10472         
10473         if (this.disabled) {
10474             input.disabled=true;
10475         }
10476         
10477         var inputblock = input;
10478         
10479         if(this.hasFeedback && !this.allowBlank){
10480             
10481             var feedback = {
10482                 tag: 'span',
10483                 cls: 'glyphicon form-control-feedback'
10484             };
10485             
10486             if(this.removable && !this.editable && !this.tickable){
10487                 inputblock = {
10488                     cls : 'has-feedback',
10489                     cn :  [
10490                         inputblock,
10491                         {
10492                             tag: 'button',
10493                             html : 'x',
10494                             cls : 'roo-combo-removable-btn close'
10495                         },
10496                         feedback
10497                     ] 
10498                 };
10499             } else {
10500                 inputblock = {
10501                     cls : 'has-feedback',
10502                     cn :  [
10503                         inputblock,
10504                         feedback
10505                     ] 
10506                 };
10507             }
10508
10509         } else {
10510             if(this.removable && !this.editable && !this.tickable){
10511                 inputblock = {
10512                     cls : 'roo-removable',
10513                     cn :  [
10514                         inputblock,
10515                         {
10516                             tag: 'button',
10517                             html : 'x',
10518                             cls : 'roo-combo-removable-btn close'
10519                         }
10520                     ] 
10521                 };
10522             }
10523         }
10524         
10525         if (this.before || this.after) {
10526             
10527             inputblock = {
10528                 cls : 'input-group',
10529                 cn :  [] 
10530             };
10531             if (this.before) {
10532                 inputblock.cn.push({
10533                     tag :'span',
10534                     cls : 'input-group-addon input-group-prepend input-group-text',
10535                     html : this.before
10536                 });
10537             }
10538             
10539             inputblock.cn.push(input);
10540             
10541             if(this.hasFeedback && !this.allowBlank){
10542                 inputblock.cls += ' has-feedback';
10543                 inputblock.cn.push(feedback);
10544             }
10545             
10546             if (this.after) {
10547                 inputblock.cn.push({
10548                     tag :'span',
10549                     cls : 'input-group-addon input-group-append input-group-text',
10550                     html : this.after
10551                 });
10552             }
10553             
10554         };
10555         
10556       
10557         
10558         var ibwrap = inputblock;
10559         
10560         if(this.multiple){
10561             ibwrap = {
10562                 tag: 'ul',
10563                 cls: 'roo-select2-choices',
10564                 cn:[
10565                     {
10566                         tag: 'li',
10567                         cls: 'roo-select2-search-field',
10568                         cn: [
10569
10570                             inputblock
10571                         ]
10572                     }
10573                 ]
10574             };
10575                 
10576         }
10577         
10578         var combobox = {
10579             cls: 'roo-select2-container input-group',
10580             cn: [
10581                  {
10582                     tag: 'input',
10583                     type : 'hidden',
10584                     cls: 'form-hidden-field'
10585                 },
10586                 ibwrap
10587             ]
10588         };
10589         
10590         if(!this.multiple && this.showToggleBtn){
10591             
10592             var caret = {
10593                         tag: 'span',
10594                         cls: 'caret'
10595              };
10596             if (this.caret != false) {
10597                 caret = {
10598                      tag: 'i',
10599                      cls: 'fa fa-' + this.caret
10600                 };
10601                 
10602             }
10603             
10604             combobox.cn.push({
10605                 tag :'span',
10606                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
10607                 cn : [
10608                     caret,
10609                     {
10610                         tag: 'span',
10611                         cls: 'combobox-clear',
10612                         cn  : [
10613                             {
10614                                 tag : 'i',
10615                                 cls: 'icon-remove'
10616                             }
10617                         ]
10618                     }
10619                 ]
10620
10621             })
10622         }
10623         
10624         if(this.multiple){
10625             combobox.cls += ' roo-select2-container-multi';
10626         }
10627          var indicator = {
10628             tag : 'i',
10629             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10630             tooltip : 'This field is required'
10631         };
10632         if (Roo.bootstrap.version == 4) {
10633             indicator = {
10634                 tag : 'i',
10635                 style : 'display:none'
10636             };
10637         }
10638         
10639         
10640         if (align ==='left' && this.fieldLabel.length) {
10641             
10642             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10643
10644             cfg.cn = [
10645                 indicator,
10646                 {
10647                     tag: 'label',
10648                     'for' :  id,
10649                     cls : 'control-label',
10650                     html : this.fieldLabel
10651
10652                 },
10653                 {
10654                     cls : "", 
10655                     cn: [
10656                         combobox
10657                     ]
10658                 }
10659
10660             ];
10661             
10662             var labelCfg = cfg.cn[1];
10663             var contentCfg = cfg.cn[2];
10664             
10665             if(this.indicatorpos == 'right'){
10666                 cfg.cn = [
10667                     {
10668                         tag: 'label',
10669                         'for' :  id,
10670                         cls : 'control-label',
10671                         cn : [
10672                             {
10673                                 tag : 'span',
10674                                 html : this.fieldLabel
10675                             },
10676                             indicator
10677                         ]
10678                     },
10679                     {
10680                         cls : "", 
10681                         cn: [
10682                             combobox
10683                         ]
10684                     }
10685
10686                 ];
10687                 
10688                 labelCfg = cfg.cn[0];
10689                 contentCfg = cfg.cn[1];
10690             }
10691             
10692             if(this.labelWidth > 12){
10693                 labelCfg.style = "width: " + this.labelWidth + 'px';
10694             }
10695             
10696             if(this.labelWidth < 13 && this.labelmd == 0){
10697                 this.labelmd = this.labelWidth;
10698             }
10699             
10700             if(this.labellg > 0){
10701                 labelCfg.cls += ' col-lg-' + this.labellg;
10702                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10703             }
10704             
10705             if(this.labelmd > 0){
10706                 labelCfg.cls += ' col-md-' + this.labelmd;
10707                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10708             }
10709             
10710             if(this.labelsm > 0){
10711                 labelCfg.cls += ' col-sm-' + this.labelsm;
10712                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10713             }
10714             
10715             if(this.labelxs > 0){
10716                 labelCfg.cls += ' col-xs-' + this.labelxs;
10717                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10718             }
10719             
10720         } else if ( this.fieldLabel.length) {
10721 //                Roo.log(" label");
10722             cfg.cn = [
10723                 indicator,
10724                {
10725                    tag: 'label',
10726                    //cls : 'input-group-addon',
10727                    html : this.fieldLabel
10728
10729                },
10730
10731                combobox
10732
10733             ];
10734             
10735             if(this.indicatorpos == 'right'){
10736                 
10737                 cfg.cn = [
10738                     {
10739                        tag: 'label',
10740                        cn : [
10741                            {
10742                                tag : 'span',
10743                                html : this.fieldLabel
10744                            },
10745                            indicator
10746                        ]
10747
10748                     },
10749                     combobox
10750
10751                 ];
10752
10753             }
10754
10755         } else {
10756             
10757 //                Roo.log(" no label && no align");
10758                 cfg = combobox
10759                      
10760                 
10761         }
10762         
10763         var settings=this;
10764         ['xs','sm','md','lg'].map(function(size){
10765             if (settings[size]) {
10766                 cfg.cls += ' col-' + size + '-' + settings[size];
10767             }
10768         });
10769         
10770         return cfg;
10771         
10772     },
10773     
10774     
10775     
10776     // private
10777     onResize : function(w, h){
10778 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10779 //        if(typeof w == 'number'){
10780 //            var x = w - this.trigger.getWidth();
10781 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10782 //            this.trigger.setStyle('left', x+'px');
10783 //        }
10784     },
10785
10786     // private
10787     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10788
10789     // private
10790     getResizeEl : function(){
10791         return this.inputEl();
10792     },
10793
10794     // private
10795     getPositionEl : function(){
10796         return this.inputEl();
10797     },
10798
10799     // private
10800     alignErrorIcon : function(){
10801         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10802     },
10803
10804     // private
10805     initEvents : function(){
10806         
10807         this.createList();
10808         
10809         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10810         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10811         if(!this.multiple && this.showToggleBtn){
10812             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10813             if(this.hideTrigger){
10814                 this.trigger.setDisplayed(false);
10815             }
10816             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10817         }
10818         
10819         if(this.multiple){
10820             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10821         }
10822         
10823         if(this.removable && !this.editable && !this.tickable){
10824             var close = this.closeTriggerEl();
10825             
10826             if(close){
10827                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10828                 close.on('click', this.removeBtnClick, this, close);
10829             }
10830         }
10831         
10832         //this.trigger.addClassOnOver('x-form-trigger-over');
10833         //this.trigger.addClassOnClick('x-form-trigger-click');
10834         
10835         //if(!this.width){
10836         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10837         //}
10838     },
10839     
10840     closeTriggerEl : function()
10841     {
10842         var close = this.el.select('.roo-combo-removable-btn', true).first();
10843         return close ? close : false;
10844     },
10845     
10846     removeBtnClick : function(e, h, el)
10847     {
10848         e.preventDefault();
10849         
10850         if(this.fireEvent("remove", this) !== false){
10851             this.reset();
10852             this.fireEvent("afterremove", this)
10853         }
10854     },
10855     
10856     createList : function()
10857     {
10858         this.list = Roo.get(document.body).createChild({
10859             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
10860             cls: 'typeahead typeahead-long dropdown-menu',
10861             style: 'display:none'
10862         });
10863         
10864         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10865         
10866     },
10867
10868     // private
10869     initTrigger : function(){
10870        
10871     },
10872
10873     // private
10874     onDestroy : function(){
10875         if(this.trigger){
10876             this.trigger.removeAllListeners();
10877           //  this.trigger.remove();
10878         }
10879         //if(this.wrap){
10880         //    this.wrap.remove();
10881         //}
10882         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10883     },
10884
10885     // private
10886     onFocus : function(){
10887         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10888         /*
10889         if(!this.mimicing){
10890             this.wrap.addClass('x-trigger-wrap-focus');
10891             this.mimicing = true;
10892             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10893             if(this.monitorTab){
10894                 this.el.on("keydown", this.checkTab, this);
10895             }
10896         }
10897         */
10898     },
10899
10900     // private
10901     checkTab : function(e){
10902         if(e.getKey() == e.TAB){
10903             this.triggerBlur();
10904         }
10905     },
10906
10907     // private
10908     onBlur : function(){
10909         // do nothing
10910     },
10911
10912     // private
10913     mimicBlur : function(e, t){
10914         /*
10915         if(!this.wrap.contains(t) && this.validateBlur()){
10916             this.triggerBlur();
10917         }
10918         */
10919     },
10920
10921     // private
10922     triggerBlur : function(){
10923         this.mimicing = false;
10924         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10925         if(this.monitorTab){
10926             this.el.un("keydown", this.checkTab, this);
10927         }
10928         //this.wrap.removeClass('x-trigger-wrap-focus');
10929         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10930     },
10931
10932     // private
10933     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10934     validateBlur : function(e, t){
10935         return true;
10936     },
10937
10938     // private
10939     onDisable : function(){
10940         this.inputEl().dom.disabled = true;
10941         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10942         //if(this.wrap){
10943         //    this.wrap.addClass('x-item-disabled');
10944         //}
10945     },
10946
10947     // private
10948     onEnable : function(){
10949         this.inputEl().dom.disabled = false;
10950         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10951         //if(this.wrap){
10952         //    this.el.removeClass('x-item-disabled');
10953         //}
10954     },
10955
10956     // private
10957     onShow : function(){
10958         var ae = this.getActionEl();
10959         
10960         if(ae){
10961             ae.dom.style.display = '';
10962             ae.dom.style.visibility = 'visible';
10963         }
10964     },
10965
10966     // private
10967     
10968     onHide : function(){
10969         var ae = this.getActionEl();
10970         ae.dom.style.display = 'none';
10971     },
10972
10973     /**
10974      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10975      * by an implementing function.
10976      * @method
10977      * @param {EventObject} e
10978      */
10979     onTriggerClick : Roo.emptyFn
10980 });
10981  /*
10982  * Based on:
10983  * Ext JS Library 1.1.1
10984  * Copyright(c) 2006-2007, Ext JS, LLC.
10985  *
10986  * Originally Released Under LGPL - original licence link has changed is not relivant.
10987  *
10988  * Fork - LGPL
10989  * <script type="text/javascript">
10990  */
10991
10992
10993 /**
10994  * @class Roo.data.SortTypes
10995  * @singleton
10996  * Defines the default sorting (casting?) comparison functions used when sorting data.
10997  */
10998 Roo.data.SortTypes = {
10999     /**
11000      * Default sort that does nothing
11001      * @param {Mixed} s The value being converted
11002      * @return {Mixed} The comparison value
11003      */
11004     none : function(s){
11005         return s;
11006     },
11007     
11008     /**
11009      * The regular expression used to strip tags
11010      * @type {RegExp}
11011      * @property
11012      */
11013     stripTagsRE : /<\/?[^>]+>/gi,
11014     
11015     /**
11016      * Strips all HTML tags to sort on text only
11017      * @param {Mixed} s The value being converted
11018      * @return {String} The comparison value
11019      */
11020     asText : function(s){
11021         return String(s).replace(this.stripTagsRE, "");
11022     },
11023     
11024     /**
11025      * Strips all HTML tags to sort on text only - Case insensitive
11026      * @param {Mixed} s The value being converted
11027      * @return {String} The comparison value
11028      */
11029     asUCText : function(s){
11030         return String(s).toUpperCase().replace(this.stripTagsRE, "");
11031     },
11032     
11033     /**
11034      * Case insensitive string
11035      * @param {Mixed} s The value being converted
11036      * @return {String} The comparison value
11037      */
11038     asUCString : function(s) {
11039         return String(s).toUpperCase();
11040     },
11041     
11042     /**
11043      * Date sorting
11044      * @param {Mixed} s The value being converted
11045      * @return {Number} The comparison value
11046      */
11047     asDate : function(s) {
11048         if(!s){
11049             return 0;
11050         }
11051         if(s instanceof Date){
11052             return s.getTime();
11053         }
11054         return Date.parse(String(s));
11055     },
11056     
11057     /**
11058      * Float sorting
11059      * @param {Mixed} s The value being converted
11060      * @return {Float} The comparison value
11061      */
11062     asFloat : function(s) {
11063         var val = parseFloat(String(s).replace(/,/g, ""));
11064         if(isNaN(val)) {
11065             val = 0;
11066         }
11067         return val;
11068     },
11069     
11070     /**
11071      * Integer sorting
11072      * @param {Mixed} s The value being converted
11073      * @return {Number} The comparison value
11074      */
11075     asInt : function(s) {
11076         var val = parseInt(String(s).replace(/,/g, ""));
11077         if(isNaN(val)) {
11078             val = 0;
11079         }
11080         return val;
11081     }
11082 };/*
11083  * Based on:
11084  * Ext JS Library 1.1.1
11085  * Copyright(c) 2006-2007, Ext JS, LLC.
11086  *
11087  * Originally Released Under LGPL - original licence link has changed is not relivant.
11088  *
11089  * Fork - LGPL
11090  * <script type="text/javascript">
11091  */
11092
11093 /**
11094 * @class Roo.data.Record
11095  * Instances of this class encapsulate both record <em>definition</em> information, and record
11096  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
11097  * to access Records cached in an {@link Roo.data.Store} object.<br>
11098  * <p>
11099  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
11100  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
11101  * objects.<br>
11102  * <p>
11103  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
11104  * @constructor
11105  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
11106  * {@link #create}. The parameters are the same.
11107  * @param {Array} data An associative Array of data values keyed by the field name.
11108  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
11109  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
11110  * not specified an integer id is generated.
11111  */
11112 Roo.data.Record = function(data, id){
11113     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
11114     this.data = data;
11115 };
11116
11117 /**
11118  * Generate a constructor for a specific record layout.
11119  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
11120  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
11121  * Each field definition object may contain the following properties: <ul>
11122  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
11123  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
11124  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
11125  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
11126  * is being used, then this is a string containing the javascript expression to reference the data relative to 
11127  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
11128  * to the data item relative to the record element. If the mapping expression is the same as the field name,
11129  * this may be omitted.</p></li>
11130  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
11131  * <ul><li>auto (Default, implies no conversion)</li>
11132  * <li>string</li>
11133  * <li>int</li>
11134  * <li>float</li>
11135  * <li>boolean</li>
11136  * <li>date</li></ul></p></li>
11137  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
11138  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
11139  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
11140  * by the Reader into an object that will be stored in the Record. It is passed the
11141  * following parameters:<ul>
11142  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
11143  * </ul></p></li>
11144  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
11145  * </ul>
11146  * <br>usage:<br><pre><code>
11147 var TopicRecord = Roo.data.Record.create(
11148     {name: 'title', mapping: 'topic_title'},
11149     {name: 'author', mapping: 'username'},
11150     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
11151     {name: 'lastPost', mapping: 'post_time', type: 'date'},
11152     {name: 'lastPoster', mapping: 'user2'},
11153     {name: 'excerpt', mapping: 'post_text'}
11154 );
11155
11156 var myNewRecord = new TopicRecord({
11157     title: 'Do my job please',
11158     author: 'noobie',
11159     totalPosts: 1,
11160     lastPost: new Date(),
11161     lastPoster: 'Animal',
11162     excerpt: 'No way dude!'
11163 });
11164 myStore.add(myNewRecord);
11165 </code></pre>
11166  * @method create
11167  * @static
11168  */
11169 Roo.data.Record.create = function(o){
11170     var f = function(){
11171         f.superclass.constructor.apply(this, arguments);
11172     };
11173     Roo.extend(f, Roo.data.Record);
11174     var p = f.prototype;
11175     p.fields = new Roo.util.MixedCollection(false, function(field){
11176         return field.name;
11177     });
11178     for(var i = 0, len = o.length; i < len; i++){
11179         p.fields.add(new Roo.data.Field(o[i]));
11180     }
11181     f.getField = function(name){
11182         return p.fields.get(name);  
11183     };
11184     return f;
11185 };
11186
11187 Roo.data.Record.AUTO_ID = 1000;
11188 Roo.data.Record.EDIT = 'edit';
11189 Roo.data.Record.REJECT = 'reject';
11190 Roo.data.Record.COMMIT = 'commit';
11191
11192 Roo.data.Record.prototype = {
11193     /**
11194      * Readonly flag - true if this record has been modified.
11195      * @type Boolean
11196      */
11197     dirty : false,
11198     editing : false,
11199     error: null,
11200     modified: null,
11201
11202     // private
11203     join : function(store){
11204         this.store = store;
11205     },
11206
11207     /**
11208      * Set the named field to the specified value.
11209      * @param {String} name The name of the field to set.
11210      * @param {Object} value The value to set the field to.
11211      */
11212     set : function(name, value){
11213         if(this.data[name] == value){
11214             return;
11215         }
11216         this.dirty = true;
11217         if(!this.modified){
11218             this.modified = {};
11219         }
11220         if(typeof this.modified[name] == 'undefined'){
11221             this.modified[name] = this.data[name];
11222         }
11223         this.data[name] = value;
11224         if(!this.editing && this.store){
11225             this.store.afterEdit(this);
11226         }       
11227     },
11228
11229     /**
11230      * Get the value of the named field.
11231      * @param {String} name The name of the field to get the value of.
11232      * @return {Object} The value of the field.
11233      */
11234     get : function(name){
11235         return this.data[name]; 
11236     },
11237
11238     // private
11239     beginEdit : function(){
11240         this.editing = true;
11241         this.modified = {}; 
11242     },
11243
11244     // private
11245     cancelEdit : function(){
11246         this.editing = false;
11247         delete this.modified;
11248     },
11249
11250     // private
11251     endEdit : function(){
11252         this.editing = false;
11253         if(this.dirty && this.store){
11254             this.store.afterEdit(this);
11255         }
11256     },
11257
11258     /**
11259      * Usually called by the {@link Roo.data.Store} which owns the Record.
11260      * Rejects all changes made to the Record since either creation, or the last commit operation.
11261      * Modified fields are reverted to their original values.
11262      * <p>
11263      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11264      * of reject operations.
11265      */
11266     reject : function(){
11267         var m = this.modified;
11268         for(var n in m){
11269             if(typeof m[n] != "function"){
11270                 this.data[n] = m[n];
11271             }
11272         }
11273         this.dirty = false;
11274         delete this.modified;
11275         this.editing = false;
11276         if(this.store){
11277             this.store.afterReject(this);
11278         }
11279     },
11280
11281     /**
11282      * Usually called by the {@link Roo.data.Store} which owns the Record.
11283      * Commits all changes made to the Record since either creation, or the last commit operation.
11284      * <p>
11285      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
11286      * of commit operations.
11287      */
11288     commit : function(){
11289         this.dirty = false;
11290         delete this.modified;
11291         this.editing = false;
11292         if(this.store){
11293             this.store.afterCommit(this);
11294         }
11295     },
11296
11297     // private
11298     hasError : function(){
11299         return this.error != null;
11300     },
11301
11302     // private
11303     clearError : function(){
11304         this.error = null;
11305     },
11306
11307     /**
11308      * Creates a copy of this record.
11309      * @param {String} id (optional) A new record id if you don't want to use this record's id
11310      * @return {Record}
11311      */
11312     copy : function(newId) {
11313         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
11314     }
11315 };/*
11316  * Based on:
11317  * Ext JS Library 1.1.1
11318  * Copyright(c) 2006-2007, Ext JS, LLC.
11319  *
11320  * Originally Released Under LGPL - original licence link has changed is not relivant.
11321  *
11322  * Fork - LGPL
11323  * <script type="text/javascript">
11324  */
11325
11326
11327
11328 /**
11329  * @class Roo.data.Store
11330  * @extends Roo.util.Observable
11331  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
11332  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
11333  * <p>
11334  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
11335  * has no knowledge of the format of the data returned by the Proxy.<br>
11336  * <p>
11337  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
11338  * instances from the data object. These records are cached and made available through accessor functions.
11339  * @constructor
11340  * Creates a new Store.
11341  * @param {Object} config A config object containing the objects needed for the Store to access data,
11342  * and read the data into Records.
11343  */
11344 Roo.data.Store = function(config){
11345     this.data = new Roo.util.MixedCollection(false);
11346     this.data.getKey = function(o){
11347         return o.id;
11348     };
11349     this.baseParams = {};
11350     // private
11351     this.paramNames = {
11352         "start" : "start",
11353         "limit" : "limit",
11354         "sort" : "sort",
11355         "dir" : "dir",
11356         "multisort" : "_multisort"
11357     };
11358
11359     if(config && config.data){
11360         this.inlineData = config.data;
11361         delete config.data;
11362     }
11363
11364     Roo.apply(this, config);
11365     
11366     if(this.reader){ // reader passed
11367         this.reader = Roo.factory(this.reader, Roo.data);
11368         this.reader.xmodule = this.xmodule || false;
11369         if(!this.recordType){
11370             this.recordType = this.reader.recordType;
11371         }
11372         if(this.reader.onMetaChange){
11373             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
11374         }
11375     }
11376
11377     if(this.recordType){
11378         this.fields = this.recordType.prototype.fields;
11379     }
11380     this.modified = [];
11381
11382     this.addEvents({
11383         /**
11384          * @event datachanged
11385          * Fires when the data cache has changed, and a widget which is using this Store
11386          * as a Record cache should refresh its view.
11387          * @param {Store} this
11388          */
11389         datachanged : true,
11390         /**
11391          * @event metachange
11392          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
11393          * @param {Store} this
11394          * @param {Object} meta The JSON metadata
11395          */
11396         metachange : true,
11397         /**
11398          * @event add
11399          * Fires when Records have been added to the Store
11400          * @param {Store} this
11401          * @param {Roo.data.Record[]} records The array of Records added
11402          * @param {Number} index The index at which the record(s) were added
11403          */
11404         add : true,
11405         /**
11406          * @event remove
11407          * Fires when a Record has been removed from the Store
11408          * @param {Store} this
11409          * @param {Roo.data.Record} record The Record that was removed
11410          * @param {Number} index The index at which the record was removed
11411          */
11412         remove : true,
11413         /**
11414          * @event update
11415          * Fires when a Record has been updated
11416          * @param {Store} this
11417          * @param {Roo.data.Record} record The Record that was updated
11418          * @param {String} operation The update operation being performed.  Value may be one of:
11419          * <pre><code>
11420  Roo.data.Record.EDIT
11421  Roo.data.Record.REJECT
11422  Roo.data.Record.COMMIT
11423          * </code></pre>
11424          */
11425         update : true,
11426         /**
11427          * @event clear
11428          * Fires when the data cache has been cleared.
11429          * @param {Store} this
11430          */
11431         clear : true,
11432         /**
11433          * @event beforeload
11434          * Fires before a request is made for a new data object.  If the beforeload handler returns false
11435          * the load action will be canceled.
11436          * @param {Store} this
11437          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11438          */
11439         beforeload : true,
11440         /**
11441          * @event beforeloadadd
11442          * Fires after a new set of Records has been loaded.
11443          * @param {Store} this
11444          * @param {Roo.data.Record[]} records The Records that were loaded
11445          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11446          */
11447         beforeloadadd : true,
11448         /**
11449          * @event load
11450          * Fires after a new set of Records has been loaded, before they are added to the store.
11451          * @param {Store} this
11452          * @param {Roo.data.Record[]} records The Records that were loaded
11453          * @param {Object} options The loading options that were specified (see {@link #load} for details)
11454          * @params {Object} return from reader
11455          */
11456         load : true,
11457         /**
11458          * @event loadexception
11459          * Fires if an exception occurs in the Proxy during loading.
11460          * Called with the signature of the Proxy's "loadexception" event.
11461          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
11462          * 
11463          * @param {Proxy} 
11464          * @param {Object} return from JsonData.reader() - success, totalRecords, records
11465          * @param {Object} load options 
11466          * @param {Object} jsonData from your request (normally this contains the Exception)
11467          */
11468         loadexception : true
11469     });
11470     
11471     if(this.proxy){
11472         this.proxy = Roo.factory(this.proxy, Roo.data);
11473         this.proxy.xmodule = this.xmodule || false;
11474         this.relayEvents(this.proxy,  ["loadexception"]);
11475     }
11476     this.sortToggle = {};
11477     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
11478
11479     Roo.data.Store.superclass.constructor.call(this);
11480
11481     if(this.inlineData){
11482         this.loadData(this.inlineData);
11483         delete this.inlineData;
11484     }
11485 };
11486
11487 Roo.extend(Roo.data.Store, Roo.util.Observable, {
11488      /**
11489     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
11490     * without a remote query - used by combo/forms at present.
11491     */
11492     
11493     /**
11494     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
11495     */
11496     /**
11497     * @cfg {Array} data Inline data to be loaded when the store is initialized.
11498     */
11499     /**
11500     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
11501     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
11502     */
11503     /**
11504     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
11505     * on any HTTP request
11506     */
11507     /**
11508     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
11509     */
11510     /**
11511     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
11512     */
11513     multiSort: false,
11514     /**
11515     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
11516     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
11517     */
11518     remoteSort : false,
11519
11520     /**
11521     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
11522      * loaded or when a record is removed. (defaults to false).
11523     */
11524     pruneModifiedRecords : false,
11525
11526     // private
11527     lastOptions : null,
11528
11529     /**
11530      * Add Records to the Store and fires the add event.
11531      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11532      */
11533     add : function(records){
11534         records = [].concat(records);
11535         for(var i = 0, len = records.length; i < len; i++){
11536             records[i].join(this);
11537         }
11538         var index = this.data.length;
11539         this.data.addAll(records);
11540         this.fireEvent("add", this, records, index);
11541     },
11542
11543     /**
11544      * Remove a Record from the Store and fires the remove event.
11545      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
11546      */
11547     remove : function(record){
11548         var index = this.data.indexOf(record);
11549         this.data.removeAt(index);
11550  
11551         if(this.pruneModifiedRecords){
11552             this.modified.remove(record);
11553         }
11554         this.fireEvent("remove", this, record, index);
11555     },
11556
11557     /**
11558      * Remove all Records from the Store and fires the clear event.
11559      */
11560     removeAll : function(){
11561         this.data.clear();
11562         if(this.pruneModifiedRecords){
11563             this.modified = [];
11564         }
11565         this.fireEvent("clear", this);
11566     },
11567
11568     /**
11569      * Inserts Records to the Store at the given index and fires the add event.
11570      * @param {Number} index The start index at which to insert the passed Records.
11571      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11572      */
11573     insert : function(index, records){
11574         records = [].concat(records);
11575         for(var i = 0, len = records.length; i < len; i++){
11576             this.data.insert(index, records[i]);
11577             records[i].join(this);
11578         }
11579         this.fireEvent("add", this, records, index);
11580     },
11581
11582     /**
11583      * Get the index within the cache of the passed Record.
11584      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11585      * @return {Number} The index of the passed Record. Returns -1 if not found.
11586      */
11587     indexOf : function(record){
11588         return this.data.indexOf(record);
11589     },
11590
11591     /**
11592      * Get the index within the cache of the Record with the passed id.
11593      * @param {String} id The id of the Record to find.
11594      * @return {Number} The index of the Record. Returns -1 if not found.
11595      */
11596     indexOfId : function(id){
11597         return this.data.indexOfKey(id);
11598     },
11599
11600     /**
11601      * Get the Record with the specified id.
11602      * @param {String} id The id of the Record to find.
11603      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11604      */
11605     getById : function(id){
11606         return this.data.key(id);
11607     },
11608
11609     /**
11610      * Get the Record at the specified index.
11611      * @param {Number} index The index of the Record to find.
11612      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11613      */
11614     getAt : function(index){
11615         return this.data.itemAt(index);
11616     },
11617
11618     /**
11619      * Returns a range of Records between specified indices.
11620      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11621      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11622      * @return {Roo.data.Record[]} An array of Records
11623      */
11624     getRange : function(start, end){
11625         return this.data.getRange(start, end);
11626     },
11627
11628     // private
11629     storeOptions : function(o){
11630         o = Roo.apply({}, o);
11631         delete o.callback;
11632         delete o.scope;
11633         this.lastOptions = o;
11634     },
11635
11636     /**
11637      * Loads the Record cache from the configured Proxy using the configured Reader.
11638      * <p>
11639      * If using remote paging, then the first load call must specify the <em>start</em>
11640      * and <em>limit</em> properties in the options.params property to establish the initial
11641      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11642      * <p>
11643      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11644      * and this call will return before the new data has been loaded. Perform any post-processing
11645      * in a callback function, or in a "load" event handler.</strong>
11646      * <p>
11647      * @param {Object} options An object containing properties which control loading options:<ul>
11648      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11649      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11650      * passed the following arguments:<ul>
11651      * <li>r : Roo.data.Record[]</li>
11652      * <li>options: Options object from the load call</li>
11653      * <li>success: Boolean success indicator</li></ul></li>
11654      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11655      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11656      * </ul>
11657      */
11658     load : function(options){
11659         options = options || {};
11660         if(this.fireEvent("beforeload", this, options) !== false){
11661             this.storeOptions(options);
11662             var p = Roo.apply(options.params || {}, this.baseParams);
11663             // if meta was not loaded from remote source.. try requesting it.
11664             if (!this.reader.metaFromRemote) {
11665                 p._requestMeta = 1;
11666             }
11667             if(this.sortInfo && this.remoteSort){
11668                 var pn = this.paramNames;
11669                 p[pn["sort"]] = this.sortInfo.field;
11670                 p[pn["dir"]] = this.sortInfo.direction;
11671             }
11672             if (this.multiSort) {
11673                 var pn = this.paramNames;
11674                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11675             }
11676             
11677             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11678         }
11679     },
11680
11681     /**
11682      * Reloads the Record cache from the configured Proxy using the configured Reader and
11683      * the options from the last load operation performed.
11684      * @param {Object} options (optional) An object containing properties which may override the options
11685      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11686      * the most recently used options are reused).
11687      */
11688     reload : function(options){
11689         this.load(Roo.applyIf(options||{}, this.lastOptions));
11690     },
11691
11692     // private
11693     // Called as a callback by the Reader during a load operation.
11694     loadRecords : function(o, options, success){
11695         if(!o || success === false){
11696             if(success !== false){
11697                 this.fireEvent("load", this, [], options, o);
11698             }
11699             if(options.callback){
11700                 options.callback.call(options.scope || this, [], options, false);
11701             }
11702             return;
11703         }
11704         // if data returned failure - throw an exception.
11705         if (o.success === false) {
11706             // show a message if no listener is registered.
11707             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11708                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11709             }
11710             // loadmask wil be hooked into this..
11711             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11712             return;
11713         }
11714         var r = o.records, t = o.totalRecords || r.length;
11715         
11716         this.fireEvent("beforeloadadd", this, r, options, o);
11717         
11718         if(!options || options.add !== true){
11719             if(this.pruneModifiedRecords){
11720                 this.modified = [];
11721             }
11722             for(var i = 0, len = r.length; i < len; i++){
11723                 r[i].join(this);
11724             }
11725             if(this.snapshot){
11726                 this.data = this.snapshot;
11727                 delete this.snapshot;
11728             }
11729             this.data.clear();
11730             this.data.addAll(r);
11731             this.totalLength = t;
11732             this.applySort();
11733             this.fireEvent("datachanged", this);
11734         }else{
11735             this.totalLength = Math.max(t, this.data.length+r.length);
11736             this.add(r);
11737         }
11738         
11739         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11740                 
11741             var e = new Roo.data.Record({});
11742
11743             e.set(this.parent.displayField, this.parent.emptyTitle);
11744             e.set(this.parent.valueField, '');
11745
11746             this.insert(0, e);
11747         }
11748             
11749         this.fireEvent("load", this, r, options, o);
11750         if(options.callback){
11751             options.callback.call(options.scope || this, r, options, true);
11752         }
11753     },
11754
11755
11756     /**
11757      * Loads data from a passed data block. A Reader which understands the format of the data
11758      * must have been configured in the constructor.
11759      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11760      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11761      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11762      */
11763     loadData : function(o, append){
11764         var r = this.reader.readRecords(o);
11765         this.loadRecords(r, {add: append}, true);
11766     },
11767
11768     /**
11769      * Gets the number of cached records.
11770      * <p>
11771      * <em>If using paging, this may not be the total size of the dataset. If the data object
11772      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11773      * the data set size</em>
11774      */
11775     getCount : function(){
11776         return this.data.length || 0;
11777     },
11778
11779     /**
11780      * Gets the total number of records in the dataset as returned by the server.
11781      * <p>
11782      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11783      * the dataset size</em>
11784      */
11785     getTotalCount : function(){
11786         return this.totalLength || 0;
11787     },
11788
11789     /**
11790      * Returns the sort state of the Store as an object with two properties:
11791      * <pre><code>
11792  field {String} The name of the field by which the Records are sorted
11793  direction {String} The sort order, "ASC" or "DESC"
11794      * </code></pre>
11795      */
11796     getSortState : function(){
11797         return this.sortInfo;
11798     },
11799
11800     // private
11801     applySort : function(){
11802         if(this.sortInfo && !this.remoteSort){
11803             var s = this.sortInfo, f = s.field;
11804             var st = this.fields.get(f).sortType;
11805             var fn = function(r1, r2){
11806                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11807                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11808             };
11809             this.data.sort(s.direction, fn);
11810             if(this.snapshot && this.snapshot != this.data){
11811                 this.snapshot.sort(s.direction, fn);
11812             }
11813         }
11814     },
11815
11816     /**
11817      * Sets the default sort column and order to be used by the next load operation.
11818      * @param {String} fieldName The name of the field to sort by.
11819      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11820      */
11821     setDefaultSort : function(field, dir){
11822         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11823     },
11824
11825     /**
11826      * Sort the Records.
11827      * If remote sorting is used, the sort is performed on the server, and the cache is
11828      * reloaded. If local sorting is used, the cache is sorted internally.
11829      * @param {String} fieldName The name of the field to sort by.
11830      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11831      */
11832     sort : function(fieldName, dir){
11833         var f = this.fields.get(fieldName);
11834         if(!dir){
11835             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11836             
11837             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11838                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11839             }else{
11840                 dir = f.sortDir;
11841             }
11842         }
11843         this.sortToggle[f.name] = dir;
11844         this.sortInfo = {field: f.name, direction: dir};
11845         if(!this.remoteSort){
11846             this.applySort();
11847             this.fireEvent("datachanged", this);
11848         }else{
11849             this.load(this.lastOptions);
11850         }
11851     },
11852
11853     /**
11854      * Calls the specified function for each of the Records in the cache.
11855      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11856      * Returning <em>false</em> aborts and exits the iteration.
11857      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11858      */
11859     each : function(fn, scope){
11860         this.data.each(fn, scope);
11861     },
11862
11863     /**
11864      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11865      * (e.g., during paging).
11866      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11867      */
11868     getModifiedRecords : function(){
11869         return this.modified;
11870     },
11871
11872     // private
11873     createFilterFn : function(property, value, anyMatch){
11874         if(!value.exec){ // not a regex
11875             value = String(value);
11876             if(value.length == 0){
11877                 return false;
11878             }
11879             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11880         }
11881         return function(r){
11882             return value.test(r.data[property]);
11883         };
11884     },
11885
11886     /**
11887      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11888      * @param {String} property A field on your records
11889      * @param {Number} start The record index to start at (defaults to 0)
11890      * @param {Number} end The last record index to include (defaults to length - 1)
11891      * @return {Number} The sum
11892      */
11893     sum : function(property, start, end){
11894         var rs = this.data.items, v = 0;
11895         start = start || 0;
11896         end = (end || end === 0) ? end : rs.length-1;
11897
11898         for(var i = start; i <= end; i++){
11899             v += (rs[i].data[property] || 0);
11900         }
11901         return v;
11902     },
11903
11904     /**
11905      * Filter the records by a specified property.
11906      * @param {String} field A field on your records
11907      * @param {String/RegExp} value Either a string that the field
11908      * should start with or a RegExp to test against the field
11909      * @param {Boolean} anyMatch True to match any part not just the beginning
11910      */
11911     filter : function(property, value, anyMatch){
11912         var fn = this.createFilterFn(property, value, anyMatch);
11913         return fn ? this.filterBy(fn) : this.clearFilter();
11914     },
11915
11916     /**
11917      * Filter by a function. The specified function will be called with each
11918      * record in this data source. If the function returns true the record is included,
11919      * otherwise it is filtered.
11920      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11921      * @param {Object} scope (optional) The scope of the function (defaults to this)
11922      */
11923     filterBy : function(fn, scope){
11924         this.snapshot = this.snapshot || this.data;
11925         this.data = this.queryBy(fn, scope||this);
11926         this.fireEvent("datachanged", this);
11927     },
11928
11929     /**
11930      * Query the records by a specified property.
11931      * @param {String} field A field on your records
11932      * @param {String/RegExp} value Either a string that the field
11933      * should start with or a RegExp to test against the field
11934      * @param {Boolean} anyMatch True to match any part not just the beginning
11935      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11936      */
11937     query : function(property, value, anyMatch){
11938         var fn = this.createFilterFn(property, value, anyMatch);
11939         return fn ? this.queryBy(fn) : this.data.clone();
11940     },
11941
11942     /**
11943      * Query by a function. The specified function will be called with each
11944      * record in this data source. If the function returns true the record is included
11945      * in the results.
11946      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11947      * @param {Object} scope (optional) The scope of the function (defaults to this)
11948       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11949      **/
11950     queryBy : function(fn, scope){
11951         var data = this.snapshot || this.data;
11952         return data.filterBy(fn, scope||this);
11953     },
11954
11955     /**
11956      * Collects unique values for a particular dataIndex from this store.
11957      * @param {String} dataIndex The property to collect
11958      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11959      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11960      * @return {Array} An array of the unique values
11961      **/
11962     collect : function(dataIndex, allowNull, bypassFilter){
11963         var d = (bypassFilter === true && this.snapshot) ?
11964                 this.snapshot.items : this.data.items;
11965         var v, sv, r = [], l = {};
11966         for(var i = 0, len = d.length; i < len; i++){
11967             v = d[i].data[dataIndex];
11968             sv = String(v);
11969             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11970                 l[sv] = true;
11971                 r[r.length] = v;
11972             }
11973         }
11974         return r;
11975     },
11976
11977     /**
11978      * Revert to a view of the Record cache with no filtering applied.
11979      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11980      */
11981     clearFilter : function(suppressEvent){
11982         if(this.snapshot && this.snapshot != this.data){
11983             this.data = this.snapshot;
11984             delete this.snapshot;
11985             if(suppressEvent !== true){
11986                 this.fireEvent("datachanged", this);
11987             }
11988         }
11989     },
11990
11991     // private
11992     afterEdit : function(record){
11993         if(this.modified.indexOf(record) == -1){
11994             this.modified.push(record);
11995         }
11996         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11997     },
11998     
11999     // private
12000     afterReject : function(record){
12001         this.modified.remove(record);
12002         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
12003     },
12004
12005     // private
12006     afterCommit : function(record){
12007         this.modified.remove(record);
12008         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
12009     },
12010
12011     /**
12012      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
12013      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
12014      */
12015     commitChanges : function(){
12016         var m = this.modified.slice(0);
12017         this.modified = [];
12018         for(var i = 0, len = m.length; i < len; i++){
12019             m[i].commit();
12020         }
12021     },
12022
12023     /**
12024      * Cancel outstanding changes on all changed records.
12025      */
12026     rejectChanges : function(){
12027         var m = this.modified.slice(0);
12028         this.modified = [];
12029         for(var i = 0, len = m.length; i < len; i++){
12030             m[i].reject();
12031         }
12032     },
12033
12034     onMetaChange : function(meta, rtype, o){
12035         this.recordType = rtype;
12036         this.fields = rtype.prototype.fields;
12037         delete this.snapshot;
12038         this.sortInfo = meta.sortInfo || this.sortInfo;
12039         this.modified = [];
12040         this.fireEvent('metachange', this, this.reader.meta);
12041     },
12042     
12043     moveIndex : function(data, type)
12044     {
12045         var index = this.indexOf(data);
12046         
12047         var newIndex = index + type;
12048         
12049         this.remove(data);
12050         
12051         this.insert(newIndex, data);
12052         
12053     }
12054 });/*
12055  * Based on:
12056  * Ext JS Library 1.1.1
12057  * Copyright(c) 2006-2007, Ext JS, LLC.
12058  *
12059  * Originally Released Under LGPL - original licence link has changed is not relivant.
12060  *
12061  * Fork - LGPL
12062  * <script type="text/javascript">
12063  */
12064
12065 /**
12066  * @class Roo.data.SimpleStore
12067  * @extends Roo.data.Store
12068  * Small helper class to make creating Stores from Array data easier.
12069  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
12070  * @cfg {Array} fields An array of field definition objects, or field name strings.
12071  * @cfg {Array} data The multi-dimensional array of data
12072  * @constructor
12073  * @param {Object} config
12074  */
12075 Roo.data.SimpleStore = function(config){
12076     Roo.data.SimpleStore.superclass.constructor.call(this, {
12077         isLocal : true,
12078         reader: new Roo.data.ArrayReader({
12079                 id: config.id
12080             },
12081             Roo.data.Record.create(config.fields)
12082         ),
12083         proxy : new Roo.data.MemoryProxy(config.data)
12084     });
12085     this.load();
12086 };
12087 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
12088  * Based on:
12089  * Ext JS Library 1.1.1
12090  * Copyright(c) 2006-2007, Ext JS, LLC.
12091  *
12092  * Originally Released Under LGPL - original licence link has changed is not relivant.
12093  *
12094  * Fork - LGPL
12095  * <script type="text/javascript">
12096  */
12097
12098 /**
12099 /**
12100  * @extends Roo.data.Store
12101  * @class Roo.data.JsonStore
12102  * Small helper class to make creating Stores for JSON data easier. <br/>
12103 <pre><code>
12104 var store = new Roo.data.JsonStore({
12105     url: 'get-images.php',
12106     root: 'images',
12107     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
12108 });
12109 </code></pre>
12110  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
12111  * JsonReader and HttpProxy (unless inline data is provided).</b>
12112  * @cfg {Array} fields An array of field definition objects, or field name strings.
12113  * @constructor
12114  * @param {Object} config
12115  */
12116 Roo.data.JsonStore = function(c){
12117     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
12118         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
12119         reader: new Roo.data.JsonReader(c, c.fields)
12120     }));
12121 };
12122 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
12123  * Based on:
12124  * Ext JS Library 1.1.1
12125  * Copyright(c) 2006-2007, Ext JS, LLC.
12126  *
12127  * Originally Released Under LGPL - original licence link has changed is not relivant.
12128  *
12129  * Fork - LGPL
12130  * <script type="text/javascript">
12131  */
12132
12133  
12134 Roo.data.Field = function(config){
12135     if(typeof config == "string"){
12136         config = {name: config};
12137     }
12138     Roo.apply(this, config);
12139     
12140     if(!this.type){
12141         this.type = "auto";
12142     }
12143     
12144     var st = Roo.data.SortTypes;
12145     // named sortTypes are supported, here we look them up
12146     if(typeof this.sortType == "string"){
12147         this.sortType = st[this.sortType];
12148     }
12149     
12150     // set default sortType for strings and dates
12151     if(!this.sortType){
12152         switch(this.type){
12153             case "string":
12154                 this.sortType = st.asUCString;
12155                 break;
12156             case "date":
12157                 this.sortType = st.asDate;
12158                 break;
12159             default:
12160                 this.sortType = st.none;
12161         }
12162     }
12163
12164     // define once
12165     var stripRe = /[\$,%]/g;
12166
12167     // prebuilt conversion function for this field, instead of
12168     // switching every time we're reading a value
12169     if(!this.convert){
12170         var cv, dateFormat = this.dateFormat;
12171         switch(this.type){
12172             case "":
12173             case "auto":
12174             case undefined:
12175                 cv = function(v){ return v; };
12176                 break;
12177             case "string":
12178                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
12179                 break;
12180             case "int":
12181                 cv = function(v){
12182                     return v !== undefined && v !== null && v !== '' ?
12183                            parseInt(String(v).replace(stripRe, ""), 10) : '';
12184                     };
12185                 break;
12186             case "float":
12187                 cv = function(v){
12188                     return v !== undefined && v !== null && v !== '' ?
12189                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
12190                     };
12191                 break;
12192             case "bool":
12193             case "boolean":
12194                 cv = function(v){ return v === true || v === "true" || v == 1; };
12195                 break;
12196             case "date":
12197                 cv = function(v){
12198                     if(!v){
12199                         return '';
12200                     }
12201                     if(v instanceof Date){
12202                         return v;
12203                     }
12204                     if(dateFormat){
12205                         if(dateFormat == "timestamp"){
12206                             return new Date(v*1000);
12207                         }
12208                         return Date.parseDate(v, dateFormat);
12209                     }
12210                     var parsed = Date.parse(v);
12211                     return parsed ? new Date(parsed) : null;
12212                 };
12213              break;
12214             
12215         }
12216         this.convert = cv;
12217     }
12218 };
12219
12220 Roo.data.Field.prototype = {
12221     dateFormat: null,
12222     defaultValue: "",
12223     mapping: null,
12224     sortType : null,
12225     sortDir : "ASC"
12226 };/*
12227  * Based on:
12228  * Ext JS Library 1.1.1
12229  * Copyright(c) 2006-2007, Ext JS, LLC.
12230  *
12231  * Originally Released Under LGPL - original licence link has changed is not relivant.
12232  *
12233  * Fork - LGPL
12234  * <script type="text/javascript">
12235  */
12236  
12237 // Base class for reading structured data from a data source.  This class is intended to be
12238 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
12239
12240 /**
12241  * @class Roo.data.DataReader
12242  * Base class for reading structured data from a data source.  This class is intended to be
12243  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
12244  */
12245
12246 Roo.data.DataReader = function(meta, recordType){
12247     
12248     this.meta = meta;
12249     
12250     this.recordType = recordType instanceof Array ? 
12251         Roo.data.Record.create(recordType) : recordType;
12252 };
12253
12254 Roo.data.DataReader.prototype = {
12255      /**
12256      * Create an empty record
12257      * @param {Object} data (optional) - overlay some values
12258      * @return {Roo.data.Record} record created.
12259      */
12260     newRow :  function(d) {
12261         var da =  {};
12262         this.recordType.prototype.fields.each(function(c) {
12263             switch( c.type) {
12264                 case 'int' : da[c.name] = 0; break;
12265                 case 'date' : da[c.name] = new Date(); break;
12266                 case 'float' : da[c.name] = 0.0; break;
12267                 case 'boolean' : da[c.name] = false; break;
12268                 default : da[c.name] = ""; break;
12269             }
12270             
12271         });
12272         return new this.recordType(Roo.apply(da, d));
12273     }
12274     
12275 };/*
12276  * Based on:
12277  * Ext JS Library 1.1.1
12278  * Copyright(c) 2006-2007, Ext JS, LLC.
12279  *
12280  * Originally Released Under LGPL - original licence link has changed is not relivant.
12281  *
12282  * Fork - LGPL
12283  * <script type="text/javascript">
12284  */
12285
12286 /**
12287  * @class Roo.data.DataProxy
12288  * @extends Roo.data.Observable
12289  * This class is an abstract base class for implementations which provide retrieval of
12290  * unformatted data objects.<br>
12291  * <p>
12292  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
12293  * (of the appropriate type which knows how to parse the data object) to provide a block of
12294  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
12295  * <p>
12296  * Custom implementations must implement the load method as described in
12297  * {@link Roo.data.HttpProxy#load}.
12298  */
12299 Roo.data.DataProxy = function(){
12300     this.addEvents({
12301         /**
12302          * @event beforeload
12303          * Fires before a network request is made to retrieve a data object.
12304          * @param {Object} This DataProxy object.
12305          * @param {Object} params The params parameter to the load function.
12306          */
12307         beforeload : true,
12308         /**
12309          * @event load
12310          * Fires before the load method's callback is called.
12311          * @param {Object} This DataProxy object.
12312          * @param {Object} o The data object.
12313          * @param {Object} arg The callback argument object passed to the load function.
12314          */
12315         load : true,
12316         /**
12317          * @event loadexception
12318          * Fires if an Exception occurs during data retrieval.
12319          * @param {Object} This DataProxy object.
12320          * @param {Object} o The data object.
12321          * @param {Object} arg The callback argument object passed to the load function.
12322          * @param {Object} e The Exception.
12323          */
12324         loadexception : true
12325     });
12326     Roo.data.DataProxy.superclass.constructor.call(this);
12327 };
12328
12329 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
12330
12331     /**
12332      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
12333      */
12334 /*
12335  * Based on:
12336  * Ext JS Library 1.1.1
12337  * Copyright(c) 2006-2007, Ext JS, LLC.
12338  *
12339  * Originally Released Under LGPL - original licence link has changed is not relivant.
12340  *
12341  * Fork - LGPL
12342  * <script type="text/javascript">
12343  */
12344 /**
12345  * @class Roo.data.MemoryProxy
12346  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
12347  * to the Reader when its load method is called.
12348  * @constructor
12349  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
12350  */
12351 Roo.data.MemoryProxy = function(data){
12352     if (data.data) {
12353         data = data.data;
12354     }
12355     Roo.data.MemoryProxy.superclass.constructor.call(this);
12356     this.data = data;
12357 };
12358
12359 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
12360     
12361     /**
12362      * Load data from the requested source (in this case an in-memory
12363      * data object passed to the constructor), read the data object into
12364      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12365      * process that block using the passed callback.
12366      * @param {Object} params This parameter is not used by the MemoryProxy class.
12367      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12368      * object into a block of Roo.data.Records.
12369      * @param {Function} callback The function into which to pass the block of Roo.data.records.
12370      * The function must be passed <ul>
12371      * <li>The Record block object</li>
12372      * <li>The "arg" argument from the load function</li>
12373      * <li>A boolean success indicator</li>
12374      * </ul>
12375      * @param {Object} scope The scope in which to call the callback
12376      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12377      */
12378     load : function(params, reader, callback, scope, arg){
12379         params = params || {};
12380         var result;
12381         try {
12382             result = reader.readRecords(params.data ? params.data :this.data);
12383         }catch(e){
12384             this.fireEvent("loadexception", this, arg, null, e);
12385             callback.call(scope, null, arg, false);
12386             return;
12387         }
12388         callback.call(scope, result, arg, true);
12389     },
12390     
12391     // private
12392     update : function(params, records){
12393         
12394     }
12395 });/*
12396  * Based on:
12397  * Ext JS Library 1.1.1
12398  * Copyright(c) 2006-2007, Ext JS, LLC.
12399  *
12400  * Originally Released Under LGPL - original licence link has changed is not relivant.
12401  *
12402  * Fork - LGPL
12403  * <script type="text/javascript">
12404  */
12405 /**
12406  * @class Roo.data.HttpProxy
12407  * @extends Roo.data.DataProxy
12408  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
12409  * configured to reference a certain URL.<br><br>
12410  * <p>
12411  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
12412  * from which the running page was served.<br><br>
12413  * <p>
12414  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
12415  * <p>
12416  * Be aware that to enable the browser to parse an XML document, the server must set
12417  * the Content-Type header in the HTTP response to "text/xml".
12418  * @constructor
12419  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
12420  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
12421  * will be used to make the request.
12422  */
12423 Roo.data.HttpProxy = function(conn){
12424     Roo.data.HttpProxy.superclass.constructor.call(this);
12425     // is conn a conn config or a real conn?
12426     this.conn = conn;
12427     this.useAjax = !conn || !conn.events;
12428   
12429 };
12430
12431 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
12432     // thse are take from connection...
12433     
12434     /**
12435      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12436      */
12437     /**
12438      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12439      * extra parameters to each request made by this object. (defaults to undefined)
12440      */
12441     /**
12442      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12443      *  to each request made by this object. (defaults to undefined)
12444      */
12445     /**
12446      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
12447      */
12448     /**
12449      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12450      */
12451      /**
12452      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12453      * @type Boolean
12454      */
12455   
12456
12457     /**
12458      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12459      * @type Boolean
12460      */
12461     /**
12462      * Return the {@link Roo.data.Connection} object being used by this Proxy.
12463      * @return {Connection} The Connection object. This object may be used to subscribe to events on
12464      * a finer-grained basis than the DataProxy events.
12465      */
12466     getConnection : function(){
12467         return this.useAjax ? Roo.Ajax : this.conn;
12468     },
12469
12470     /**
12471      * Load data from the configured {@link Roo.data.Connection}, read the data object into
12472      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
12473      * process that block using the passed callback.
12474      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12475      * for the request to the remote server.
12476      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12477      * object into a block of Roo.data.Records.
12478      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12479      * The function must be passed <ul>
12480      * <li>The Record block object</li>
12481      * <li>The "arg" argument from the load function</li>
12482      * <li>A boolean success indicator</li>
12483      * </ul>
12484      * @param {Object} scope The scope in which to call the callback
12485      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12486      */
12487     load : function(params, reader, callback, scope, arg){
12488         if(this.fireEvent("beforeload", this, params) !== false){
12489             var  o = {
12490                 params : params || {},
12491                 request: {
12492                     callback : callback,
12493                     scope : scope,
12494                     arg : arg
12495                 },
12496                 reader: reader,
12497                 callback : this.loadResponse,
12498                 scope: this
12499             };
12500             if(this.useAjax){
12501                 Roo.applyIf(o, this.conn);
12502                 if(this.activeRequest){
12503                     Roo.Ajax.abort(this.activeRequest);
12504                 }
12505                 this.activeRequest = Roo.Ajax.request(o);
12506             }else{
12507                 this.conn.request(o);
12508             }
12509         }else{
12510             callback.call(scope||this, null, arg, false);
12511         }
12512     },
12513
12514     // private
12515     loadResponse : function(o, success, response){
12516         delete this.activeRequest;
12517         if(!success){
12518             this.fireEvent("loadexception", this, o, response);
12519             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12520             return;
12521         }
12522         var result;
12523         try {
12524             result = o.reader.read(response);
12525         }catch(e){
12526             this.fireEvent("loadexception", this, o, response, e);
12527             o.request.callback.call(o.request.scope, null, o.request.arg, false);
12528             return;
12529         }
12530         
12531         this.fireEvent("load", this, o, o.request.arg);
12532         o.request.callback.call(o.request.scope, result, o.request.arg, true);
12533     },
12534
12535     // private
12536     update : function(dataSet){
12537
12538     },
12539
12540     // private
12541     updateResponse : function(dataSet){
12542
12543     }
12544 });/*
12545  * Based on:
12546  * Ext JS Library 1.1.1
12547  * Copyright(c) 2006-2007, Ext JS, LLC.
12548  *
12549  * Originally Released Under LGPL - original licence link has changed is not relivant.
12550  *
12551  * Fork - LGPL
12552  * <script type="text/javascript">
12553  */
12554
12555 /**
12556  * @class Roo.data.ScriptTagProxy
12557  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12558  * other than the originating domain of the running page.<br><br>
12559  * <p>
12560  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
12561  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12562  * <p>
12563  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12564  * source code that is used as the source inside a &lt;script> tag.<br><br>
12565  * <p>
12566  * In order for the browser to process the returned data, the server must wrap the data object
12567  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12568  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12569  * depending on whether the callback name was passed:
12570  * <p>
12571  * <pre><code>
12572 boolean scriptTag = false;
12573 String cb = request.getParameter("callback");
12574 if (cb != null) {
12575     scriptTag = true;
12576     response.setContentType("text/javascript");
12577 } else {
12578     response.setContentType("application/x-json");
12579 }
12580 Writer out = response.getWriter();
12581 if (scriptTag) {
12582     out.write(cb + "(");
12583 }
12584 out.print(dataBlock.toJsonString());
12585 if (scriptTag) {
12586     out.write(");");
12587 }
12588 </pre></code>
12589  *
12590  * @constructor
12591  * @param {Object} config A configuration object.
12592  */
12593 Roo.data.ScriptTagProxy = function(config){
12594     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12595     Roo.apply(this, config);
12596     this.head = document.getElementsByTagName("head")[0];
12597 };
12598
12599 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12600
12601 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12602     /**
12603      * @cfg {String} url The URL from which to request the data object.
12604      */
12605     /**
12606      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12607      */
12608     timeout : 30000,
12609     /**
12610      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12611      * the server the name of the callback function set up by the load call to process the returned data object.
12612      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12613      * javascript output which calls this named function passing the data object as its only parameter.
12614      */
12615     callbackParam : "callback",
12616     /**
12617      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12618      * name to the request.
12619      */
12620     nocache : true,
12621
12622     /**
12623      * Load data from the configured URL, read the data object into
12624      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12625      * process that block using the passed callback.
12626      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12627      * for the request to the remote server.
12628      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12629      * object into a block of Roo.data.Records.
12630      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12631      * The function must be passed <ul>
12632      * <li>The Record block object</li>
12633      * <li>The "arg" argument from the load function</li>
12634      * <li>A boolean success indicator</li>
12635      * </ul>
12636      * @param {Object} scope The scope in which to call the callback
12637      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12638      */
12639     load : function(params, reader, callback, scope, arg){
12640         if(this.fireEvent("beforeload", this, params) !== false){
12641
12642             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12643
12644             var url = this.url;
12645             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12646             if(this.nocache){
12647                 url += "&_dc=" + (new Date().getTime());
12648             }
12649             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12650             var trans = {
12651                 id : transId,
12652                 cb : "stcCallback"+transId,
12653                 scriptId : "stcScript"+transId,
12654                 params : params,
12655                 arg : arg,
12656                 url : url,
12657                 callback : callback,
12658                 scope : scope,
12659                 reader : reader
12660             };
12661             var conn = this;
12662
12663             window[trans.cb] = function(o){
12664                 conn.handleResponse(o, trans);
12665             };
12666
12667             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12668
12669             if(this.autoAbort !== false){
12670                 this.abort();
12671             }
12672
12673             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12674
12675             var script = document.createElement("script");
12676             script.setAttribute("src", url);
12677             script.setAttribute("type", "text/javascript");
12678             script.setAttribute("id", trans.scriptId);
12679             this.head.appendChild(script);
12680
12681             this.trans = trans;
12682         }else{
12683             callback.call(scope||this, null, arg, false);
12684         }
12685     },
12686
12687     // private
12688     isLoading : function(){
12689         return this.trans ? true : false;
12690     },
12691
12692     /**
12693      * Abort the current server request.
12694      */
12695     abort : function(){
12696         if(this.isLoading()){
12697             this.destroyTrans(this.trans);
12698         }
12699     },
12700
12701     // private
12702     destroyTrans : function(trans, isLoaded){
12703         this.head.removeChild(document.getElementById(trans.scriptId));
12704         clearTimeout(trans.timeoutId);
12705         if(isLoaded){
12706             window[trans.cb] = undefined;
12707             try{
12708                 delete window[trans.cb];
12709             }catch(e){}
12710         }else{
12711             // if hasn't been loaded, wait for load to remove it to prevent script error
12712             window[trans.cb] = function(){
12713                 window[trans.cb] = undefined;
12714                 try{
12715                     delete window[trans.cb];
12716                 }catch(e){}
12717             };
12718         }
12719     },
12720
12721     // private
12722     handleResponse : function(o, trans){
12723         this.trans = false;
12724         this.destroyTrans(trans, true);
12725         var result;
12726         try {
12727             result = trans.reader.readRecords(o);
12728         }catch(e){
12729             this.fireEvent("loadexception", this, o, trans.arg, e);
12730             trans.callback.call(trans.scope||window, null, trans.arg, false);
12731             return;
12732         }
12733         this.fireEvent("load", this, o, trans.arg);
12734         trans.callback.call(trans.scope||window, result, trans.arg, true);
12735     },
12736
12737     // private
12738     handleFailure : function(trans){
12739         this.trans = false;
12740         this.destroyTrans(trans, false);
12741         this.fireEvent("loadexception", this, null, trans.arg);
12742         trans.callback.call(trans.scope||window, null, trans.arg, false);
12743     }
12744 });/*
12745  * Based on:
12746  * Ext JS Library 1.1.1
12747  * Copyright(c) 2006-2007, Ext JS, LLC.
12748  *
12749  * Originally Released Under LGPL - original licence link has changed is not relivant.
12750  *
12751  * Fork - LGPL
12752  * <script type="text/javascript">
12753  */
12754
12755 /**
12756  * @class Roo.data.JsonReader
12757  * @extends Roo.data.DataReader
12758  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12759  * based on mappings in a provided Roo.data.Record constructor.
12760  * 
12761  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12762  * in the reply previously. 
12763  * 
12764  * <p>
12765  * Example code:
12766  * <pre><code>
12767 var RecordDef = Roo.data.Record.create([
12768     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12769     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12770 ]);
12771 var myReader = new Roo.data.JsonReader({
12772     totalProperty: "results",    // The property which contains the total dataset size (optional)
12773     root: "rows",                // The property which contains an Array of row objects
12774     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12775 }, RecordDef);
12776 </code></pre>
12777  * <p>
12778  * This would consume a JSON file like this:
12779  * <pre><code>
12780 { 'results': 2, 'rows': [
12781     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12782     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12783 }
12784 </code></pre>
12785  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12786  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12787  * paged from the remote server.
12788  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12789  * @cfg {String} root name of the property which contains the Array of row objects.
12790  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12791  * @cfg {Array} fields Array of field definition objects
12792  * @constructor
12793  * Create a new JsonReader
12794  * @param {Object} meta Metadata configuration options
12795  * @param {Object} recordType Either an Array of field definition objects,
12796  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12797  */
12798 Roo.data.JsonReader = function(meta, recordType){
12799     
12800     meta = meta || {};
12801     // set some defaults:
12802     Roo.applyIf(meta, {
12803         totalProperty: 'total',
12804         successProperty : 'success',
12805         root : 'data',
12806         id : 'id'
12807     });
12808     
12809     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12810 };
12811 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12812     
12813     /**
12814      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12815      * Used by Store query builder to append _requestMeta to params.
12816      * 
12817      */
12818     metaFromRemote : false,
12819     /**
12820      * This method is only used by a DataProxy which has retrieved data from a remote server.
12821      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12822      * @return {Object} data A data block which is used by an Roo.data.Store object as
12823      * a cache of Roo.data.Records.
12824      */
12825     read : function(response){
12826         var json = response.responseText;
12827        
12828         var o = /* eval:var:o */ eval("("+json+")");
12829         if(!o) {
12830             throw {message: "JsonReader.read: Json object not found"};
12831         }
12832         
12833         if(o.metaData){
12834             
12835             delete this.ef;
12836             this.metaFromRemote = true;
12837             this.meta = o.metaData;
12838             this.recordType = Roo.data.Record.create(o.metaData.fields);
12839             this.onMetaChange(this.meta, this.recordType, o);
12840         }
12841         return this.readRecords(o);
12842     },
12843
12844     // private function a store will implement
12845     onMetaChange : function(meta, recordType, o){
12846
12847     },
12848
12849     /**
12850          * @ignore
12851          */
12852     simpleAccess: function(obj, subsc) {
12853         return obj[subsc];
12854     },
12855
12856         /**
12857          * @ignore
12858          */
12859     getJsonAccessor: function(){
12860         var re = /[\[\.]/;
12861         return function(expr) {
12862             try {
12863                 return(re.test(expr))
12864                     ? new Function("obj", "return obj." + expr)
12865                     : function(obj){
12866                         return obj[expr];
12867                     };
12868             } catch(e){}
12869             return Roo.emptyFn;
12870         };
12871     }(),
12872
12873     /**
12874      * Create a data block containing Roo.data.Records from an XML document.
12875      * @param {Object} o An object which contains an Array of row objects in the property specified
12876      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12877      * which contains the total size of the dataset.
12878      * @return {Object} data A data block which is used by an Roo.data.Store object as
12879      * a cache of Roo.data.Records.
12880      */
12881     readRecords : function(o){
12882         /**
12883          * After any data loads, the raw JSON data is available for further custom processing.
12884          * @type Object
12885          */
12886         this.o = o;
12887         var s = this.meta, Record = this.recordType,
12888             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12889
12890 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12891         if (!this.ef) {
12892             if(s.totalProperty) {
12893                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12894                 }
12895                 if(s.successProperty) {
12896                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12897                 }
12898                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12899                 if (s.id) {
12900                         var g = this.getJsonAccessor(s.id);
12901                         this.getId = function(rec) {
12902                                 var r = g(rec);  
12903                                 return (r === undefined || r === "") ? null : r;
12904                         };
12905                 } else {
12906                         this.getId = function(){return null;};
12907                 }
12908             this.ef = [];
12909             for(var jj = 0; jj < fl; jj++){
12910                 f = fi[jj];
12911                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12912                 this.ef[jj] = this.getJsonAccessor(map);
12913             }
12914         }
12915
12916         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12917         if(s.totalProperty){
12918             var vt = parseInt(this.getTotal(o), 10);
12919             if(!isNaN(vt)){
12920                 totalRecords = vt;
12921             }
12922         }
12923         if(s.successProperty){
12924             var vs = this.getSuccess(o);
12925             if(vs === false || vs === 'false'){
12926                 success = false;
12927             }
12928         }
12929         var records = [];
12930         for(var i = 0; i < c; i++){
12931                 var n = root[i];
12932             var values = {};
12933             var id = this.getId(n);
12934             for(var j = 0; j < fl; j++){
12935                 f = fi[j];
12936             var v = this.ef[j](n);
12937             if (!f.convert) {
12938                 Roo.log('missing convert for ' + f.name);
12939                 Roo.log(f);
12940                 continue;
12941             }
12942             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12943             }
12944             var record = new Record(values, id);
12945             record.json = n;
12946             records[i] = record;
12947         }
12948         return {
12949             raw : o,
12950             success : success,
12951             records : records,
12952             totalRecords : totalRecords
12953         };
12954     }
12955 });/*
12956  * Based on:
12957  * Ext JS Library 1.1.1
12958  * Copyright(c) 2006-2007, Ext JS, LLC.
12959  *
12960  * Originally Released Under LGPL - original licence link has changed is not relivant.
12961  *
12962  * Fork - LGPL
12963  * <script type="text/javascript">
12964  */
12965
12966 /**
12967  * @class Roo.data.ArrayReader
12968  * @extends Roo.data.DataReader
12969  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12970  * Each element of that Array represents a row of data fields. The
12971  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12972  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12973  * <p>
12974  * Example code:.
12975  * <pre><code>
12976 var RecordDef = Roo.data.Record.create([
12977     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12978     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12979 ]);
12980 var myReader = new Roo.data.ArrayReader({
12981     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12982 }, RecordDef);
12983 </code></pre>
12984  * <p>
12985  * This would consume an Array like this:
12986  * <pre><code>
12987 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12988   </code></pre>
12989  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12990  * @constructor
12991  * Create a new JsonReader
12992  * @param {Object} meta Metadata configuration options.
12993  * @param {Object} recordType Either an Array of field definition objects
12994  * @cfg {Array} fields Array of field definition objects
12995  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12996  * as specified to {@link Roo.data.Record#create},
12997  * or an {@link Roo.data.Record} object
12998  * created using {@link Roo.data.Record#create}.
12999  */
13000 Roo.data.ArrayReader = function(meta, recordType){
13001     
13002      
13003     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
13004 };
13005
13006 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
13007     /**
13008      * Create a data block containing Roo.data.Records from an XML document.
13009      * @param {Object} o An Array of row objects which represents the dataset.
13010      * @return {Object} data A data block which is used by an Roo.data.Store object as
13011      * a cache of Roo.data.Records.
13012      */
13013     readRecords : function(o){
13014         var sid = this.meta ? this.meta.id : null;
13015         var recordType = this.recordType, fields = recordType.prototype.fields;
13016         var records = [];
13017         var root = o;
13018             for(var i = 0; i < root.length; i++){
13019                     var n = root[i];
13020                 var values = {};
13021                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
13022                 for(var j = 0, jlen = fields.length; j < jlen; j++){
13023                 var f = fields.items[j];
13024                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
13025                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
13026                 v = f.convert(v);
13027                 values[f.name] = v;
13028             }
13029                 var record = new recordType(values, id);
13030                 record.json = n;
13031                 records[records.length] = record;
13032             }
13033             return {
13034                 records : records,
13035                 totalRecords : records.length
13036             };
13037     }
13038 });/*
13039  * - LGPL
13040  * * 
13041  */
13042
13043 /**
13044  * @class Roo.bootstrap.ComboBox
13045  * @extends Roo.bootstrap.TriggerField
13046  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
13047  * @cfg {Boolean} append (true|false) default false
13048  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
13049  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
13050  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
13051  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
13052  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
13053  * @cfg {Boolean} animate default true
13054  * @cfg {Boolean} emptyResultText only for touch device
13055  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
13056  * @cfg {String} emptyTitle default ''
13057  * @constructor
13058  * Create a new ComboBox.
13059  * @param {Object} config Configuration options
13060  */
13061 Roo.bootstrap.ComboBox = function(config){
13062     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
13063     this.addEvents({
13064         /**
13065          * @event expand
13066          * Fires when the dropdown list is expanded
13067         * @param {Roo.bootstrap.ComboBox} combo This combo box
13068         */
13069         'expand' : true,
13070         /**
13071          * @event collapse
13072          * Fires when the dropdown list is collapsed
13073         * @param {Roo.bootstrap.ComboBox} combo This combo box
13074         */
13075         'collapse' : true,
13076         /**
13077          * @event beforeselect
13078          * Fires before a list item is selected. Return false to cancel the selection.
13079         * @param {Roo.bootstrap.ComboBox} combo This combo box
13080         * @param {Roo.data.Record} record The data record returned from the underlying store
13081         * @param {Number} index The index of the selected item in the dropdown list
13082         */
13083         'beforeselect' : true,
13084         /**
13085          * @event select
13086          * Fires when a list item is selected
13087         * @param {Roo.bootstrap.ComboBox} combo This combo box
13088         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
13089         * @param {Number} index The index of the selected item in the dropdown list
13090         */
13091         'select' : true,
13092         /**
13093          * @event beforequery
13094          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
13095          * The event object passed has these properties:
13096         * @param {Roo.bootstrap.ComboBox} combo This combo box
13097         * @param {String} query The query
13098         * @param {Boolean} forceAll true to force "all" query
13099         * @param {Boolean} cancel true to cancel the query
13100         * @param {Object} e The query event object
13101         */
13102         'beforequery': true,
13103          /**
13104          * @event add
13105          * Fires when the 'add' icon is pressed (add a listener to enable add button)
13106         * @param {Roo.bootstrap.ComboBox} combo This combo box
13107         */
13108         'add' : true,
13109         /**
13110          * @event edit
13111          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
13112         * @param {Roo.bootstrap.ComboBox} combo This combo box
13113         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
13114         */
13115         'edit' : true,
13116         /**
13117          * @event remove
13118          * Fires when the remove value from the combobox array
13119         * @param {Roo.bootstrap.ComboBox} combo This combo box
13120         */
13121         'remove' : true,
13122         /**
13123          * @event afterremove
13124          * Fires when the remove value from the combobox array
13125         * @param {Roo.bootstrap.ComboBox} combo This combo box
13126         */
13127         'afterremove' : true,
13128         /**
13129          * @event specialfilter
13130          * Fires when specialfilter
13131             * @param {Roo.bootstrap.ComboBox} combo This combo box
13132             */
13133         'specialfilter' : true,
13134         /**
13135          * @event tick
13136          * Fires when tick the element
13137             * @param {Roo.bootstrap.ComboBox} combo This combo box
13138             */
13139         'tick' : true,
13140         /**
13141          * @event touchviewdisplay
13142          * Fires when touch view require special display (default is using displayField)
13143             * @param {Roo.bootstrap.ComboBox} combo This combo box
13144             * @param {Object} cfg set html .
13145             */
13146         'touchviewdisplay' : true
13147         
13148     });
13149     
13150     this.item = [];
13151     this.tickItems = [];
13152     
13153     this.selectedIndex = -1;
13154     if(this.mode == 'local'){
13155         if(config.queryDelay === undefined){
13156             this.queryDelay = 10;
13157         }
13158         if(config.minChars === undefined){
13159             this.minChars = 0;
13160         }
13161     }
13162 };
13163
13164 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
13165      
13166     /**
13167      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
13168      * rendering into an Roo.Editor, defaults to false)
13169      */
13170     /**
13171      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
13172      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
13173      */
13174     /**
13175      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
13176      */
13177     /**
13178      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
13179      * the dropdown list (defaults to undefined, with no header element)
13180      */
13181
13182      /**
13183      * @cfg {String/Roo.Template} tpl The template to use to render the output
13184      */
13185      
13186      /**
13187      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
13188      */
13189     listWidth: undefined,
13190     /**
13191      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
13192      * mode = 'remote' or 'text' if mode = 'local')
13193      */
13194     displayField: undefined,
13195     
13196     /**
13197      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
13198      * mode = 'remote' or 'value' if mode = 'local'). 
13199      * Note: use of a valueField requires the user make a selection
13200      * in order for a value to be mapped.
13201      */
13202     valueField: undefined,
13203     /**
13204      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
13205      */
13206     modalTitle : '',
13207     
13208     /**
13209      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
13210      * field's data value (defaults to the underlying DOM element's name)
13211      */
13212     hiddenName: undefined,
13213     /**
13214      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
13215      */
13216     listClass: '',
13217     /**
13218      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
13219      */
13220     selectedClass: 'active',
13221     
13222     /**
13223      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
13224      */
13225     shadow:'sides',
13226     /**
13227      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
13228      * anchor positions (defaults to 'tl-bl')
13229      */
13230     listAlign: 'tl-bl?',
13231     /**
13232      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
13233      */
13234     maxHeight: 300,
13235     /**
13236      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
13237      * query specified by the allQuery config option (defaults to 'query')
13238      */
13239     triggerAction: 'query',
13240     /**
13241      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
13242      * (defaults to 4, does not apply if editable = false)
13243      */
13244     minChars : 4,
13245     /**
13246      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
13247      * delay (typeAheadDelay) if it matches a known value (defaults to false)
13248      */
13249     typeAhead: false,
13250     /**
13251      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
13252      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
13253      */
13254     queryDelay: 500,
13255     /**
13256      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
13257      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
13258      */
13259     pageSize: 0,
13260     /**
13261      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
13262      * when editable = true (defaults to false)
13263      */
13264     selectOnFocus:false,
13265     /**
13266      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
13267      */
13268     queryParam: 'query',
13269     /**
13270      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
13271      * when mode = 'remote' (defaults to 'Loading...')
13272      */
13273     loadingText: 'Loading...',
13274     /**
13275      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
13276      */
13277     resizable: false,
13278     /**
13279      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
13280      */
13281     handleHeight : 8,
13282     /**
13283      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
13284      * traditional select (defaults to true)
13285      */
13286     editable: true,
13287     /**
13288      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
13289      */
13290     allQuery: '',
13291     /**
13292      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
13293      */
13294     mode: 'remote',
13295     /**
13296      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
13297      * listWidth has a higher value)
13298      */
13299     minListWidth : 70,
13300     /**
13301      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
13302      * allow the user to set arbitrary text into the field (defaults to false)
13303      */
13304     forceSelection:false,
13305     /**
13306      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
13307      * if typeAhead = true (defaults to 250)
13308      */
13309     typeAheadDelay : 250,
13310     /**
13311      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
13312      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
13313      */
13314     valueNotFoundText : undefined,
13315     /**
13316      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
13317      */
13318     blockFocus : false,
13319     
13320     /**
13321      * @cfg {Boolean} disableClear Disable showing of clear button.
13322      */
13323     disableClear : false,
13324     /**
13325      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
13326      */
13327     alwaysQuery : false,
13328     
13329     /**
13330      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
13331      */
13332     multiple : false,
13333     
13334     /**
13335      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
13336      */
13337     invalidClass : "has-warning",
13338     
13339     /**
13340      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
13341      */
13342     validClass : "has-success",
13343     
13344     /**
13345      * @cfg {Boolean} specialFilter (true|false) special filter default false
13346      */
13347     specialFilter : false,
13348     
13349     /**
13350      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
13351      */
13352     mobileTouchView : true,
13353     
13354     /**
13355      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
13356      */
13357     useNativeIOS : false,
13358     
13359     /**
13360      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
13361      */
13362     mobile_restrict_height : false,
13363     
13364     ios_options : false,
13365     
13366     //private
13367     addicon : false,
13368     editicon: false,
13369     
13370     page: 0,
13371     hasQuery: false,
13372     append: false,
13373     loadNext: false,
13374     autoFocus : true,
13375     tickable : false,
13376     btnPosition : 'right',
13377     triggerList : true,
13378     showToggleBtn : true,
13379     animate : true,
13380     emptyResultText: 'Empty',
13381     triggerText : 'Select',
13382     emptyTitle : '',
13383     
13384     // element that contains real text value.. (when hidden is used..)
13385     
13386     getAutoCreate : function()
13387     {   
13388         var cfg = false;
13389         //render
13390         /*
13391          * Render classic select for iso
13392          */
13393         
13394         if(Roo.isIOS && this.useNativeIOS){
13395             cfg = this.getAutoCreateNativeIOS();
13396             return cfg;
13397         }
13398         
13399         /*
13400          * Touch Devices
13401          */
13402         
13403         if(Roo.isTouch && this.mobileTouchView){
13404             cfg = this.getAutoCreateTouchView();
13405             return cfg;;
13406         }
13407         
13408         /*
13409          *  Normal ComboBox
13410          */
13411         if(!this.tickable){
13412             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
13413             return cfg;
13414         }
13415         
13416         /*
13417          *  ComboBox with tickable selections
13418          */
13419              
13420         var align = this.labelAlign || this.parentLabelAlign();
13421         
13422         cfg = {
13423             cls : 'form-group roo-combobox-tickable' //input-group
13424         };
13425         
13426         var btn_text_select = '';
13427         var btn_text_done = '';
13428         var btn_text_cancel = '';
13429         
13430         if (this.btn_text_show) {
13431             btn_text_select = 'Select';
13432             btn_text_done = 'Done';
13433             btn_text_cancel = 'Cancel'; 
13434         }
13435         
13436         var buttons = {
13437             tag : 'div',
13438             cls : 'tickable-buttons',
13439             cn : [
13440                 {
13441                     tag : 'button',
13442                     type : 'button',
13443                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
13444                     //html : this.triggerText
13445                     html: btn_text_select
13446                 },
13447                 {
13448                     tag : 'button',
13449                     type : 'button',
13450                     name : 'ok',
13451                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
13452                     //html : 'Done'
13453                     html: btn_text_done
13454                 },
13455                 {
13456                     tag : 'button',
13457                     type : 'button',
13458                     name : 'cancel',
13459                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
13460                     //html : 'Cancel'
13461                     html: btn_text_cancel
13462                 }
13463             ]
13464         };
13465         
13466         if(this.editable){
13467             buttons.cn.unshift({
13468                 tag: 'input',
13469                 cls: 'roo-select2-search-field-input'
13470             });
13471         }
13472         
13473         var _this = this;
13474         
13475         Roo.each(buttons.cn, function(c){
13476             if (_this.size) {
13477                 c.cls += ' btn-' + _this.size;
13478             }
13479
13480             if (_this.disabled) {
13481                 c.disabled = true;
13482             }
13483         });
13484         
13485         var box = {
13486             tag: 'div',
13487             style : 'display: contents',
13488             cn: [
13489                 {
13490                     tag: 'input',
13491                     type : 'hidden',
13492                     cls: 'form-hidden-field'
13493                 },
13494                 {
13495                     tag: 'ul',
13496                     cls: 'roo-select2-choices',
13497                     cn:[
13498                         {
13499                             tag: 'li',
13500                             cls: 'roo-select2-search-field',
13501                             cn: [
13502                                 buttons
13503                             ]
13504                         }
13505                     ]
13506                 }
13507             ]
13508         };
13509         
13510         var combobox = {
13511             cls: 'roo-select2-container input-group roo-select2-container-multi',
13512             cn: [
13513                 
13514                 box
13515 //                {
13516 //                    tag: 'ul',
13517 //                    cls: 'typeahead typeahead-long dropdown-menu',
13518 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
13519 //                }
13520             ]
13521         };
13522         
13523         if(this.hasFeedback && !this.allowBlank){
13524             
13525             var feedback = {
13526                 tag: 'span',
13527                 cls: 'glyphicon form-control-feedback'
13528             };
13529
13530             combobox.cn.push(feedback);
13531         }
13532         
13533         var indicator = {
13534             tag : 'i',
13535             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13536             tooltip : 'This field is required'
13537         };
13538         if (Roo.bootstrap.version == 4) {
13539             indicator = {
13540                 tag : 'i',
13541                 style : 'display:none'
13542             };
13543         }
13544         if (align ==='left' && this.fieldLabel.length) {
13545             
13546             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13547             
13548             cfg.cn = [
13549                 indicator,
13550                 {
13551                     tag: 'label',
13552                     'for' :  id,
13553                     cls : 'control-label col-form-label',
13554                     html : this.fieldLabel
13555
13556                 },
13557                 {
13558                     cls : "", 
13559                     cn: [
13560                         combobox
13561                     ]
13562                 }
13563
13564             ];
13565             
13566             var labelCfg = cfg.cn[1];
13567             var contentCfg = cfg.cn[2];
13568             
13569
13570             if(this.indicatorpos == 'right'){
13571                 
13572                 cfg.cn = [
13573                     {
13574                         tag: 'label',
13575                         'for' :  id,
13576                         cls : 'control-label col-form-label',
13577                         cn : [
13578                             {
13579                                 tag : 'span',
13580                                 html : this.fieldLabel
13581                             },
13582                             indicator
13583                         ]
13584                     },
13585                     {
13586                         cls : "",
13587                         cn: [
13588                             combobox
13589                         ]
13590                     }
13591
13592                 ];
13593                 
13594                 
13595                 
13596                 labelCfg = cfg.cn[0];
13597                 contentCfg = cfg.cn[1];
13598             
13599             }
13600             
13601             if(this.labelWidth > 12){
13602                 labelCfg.style = "width: " + this.labelWidth + 'px';
13603             }
13604             
13605             if(this.labelWidth < 13 && this.labelmd == 0){
13606                 this.labelmd = this.labelWidth;
13607             }
13608             
13609             if(this.labellg > 0){
13610                 labelCfg.cls += ' col-lg-' + this.labellg;
13611                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13612             }
13613             
13614             if(this.labelmd > 0){
13615                 labelCfg.cls += ' col-md-' + this.labelmd;
13616                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13617             }
13618             
13619             if(this.labelsm > 0){
13620                 labelCfg.cls += ' col-sm-' + this.labelsm;
13621                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13622             }
13623             
13624             if(this.labelxs > 0){
13625                 labelCfg.cls += ' col-xs-' + this.labelxs;
13626                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13627             }
13628                 
13629                 
13630         } else if ( this.fieldLabel.length) {
13631 //                Roo.log(" label");
13632                  cfg.cn = [
13633                    indicator,
13634                     {
13635                         tag: 'label',
13636                         //cls : 'input-group-addon',
13637                         html : this.fieldLabel
13638                     },
13639                     combobox
13640                 ];
13641                 
13642                 if(this.indicatorpos == 'right'){
13643                     cfg.cn = [
13644                         {
13645                             tag: 'label',
13646                             //cls : 'input-group-addon',
13647                             html : this.fieldLabel
13648                         },
13649                         indicator,
13650                         combobox
13651                     ];
13652                     
13653                 }
13654
13655         } else {
13656             
13657 //                Roo.log(" no label && no align");
13658                 cfg = combobox
13659                      
13660                 
13661         }
13662          
13663         var settings=this;
13664         ['xs','sm','md','lg'].map(function(size){
13665             if (settings[size]) {
13666                 cfg.cls += ' col-' + size + '-' + settings[size];
13667             }
13668         });
13669         
13670         return cfg;
13671         
13672     },
13673     
13674     _initEventsCalled : false,
13675     
13676     // private
13677     initEvents: function()
13678     {   
13679         if (this._initEventsCalled) { // as we call render... prevent looping...
13680             return;
13681         }
13682         this._initEventsCalled = true;
13683         
13684         if (!this.store) {
13685             throw "can not find store for combo";
13686         }
13687         
13688         this.indicator = this.indicatorEl();
13689         
13690         this.store = Roo.factory(this.store, Roo.data);
13691         this.store.parent = this;
13692         
13693         // if we are building from html. then this element is so complex, that we can not really
13694         // use the rendered HTML.
13695         // so we have to trash and replace the previous code.
13696         if (Roo.XComponent.build_from_html) {
13697             // remove this element....
13698             var e = this.el.dom, k=0;
13699             while (e ) { e = e.previousSibling;  ++k;}
13700
13701             this.el.remove();
13702             
13703             this.el=false;
13704             this.rendered = false;
13705             
13706             this.render(this.parent().getChildContainer(true), k);
13707         }
13708         
13709         if(Roo.isIOS && this.useNativeIOS){
13710             this.initIOSView();
13711             return;
13712         }
13713         
13714         /*
13715          * Touch Devices
13716          */
13717         
13718         if(Roo.isTouch && this.mobileTouchView){
13719             this.initTouchView();
13720             return;
13721         }
13722         
13723         if(this.tickable){
13724             this.initTickableEvents();
13725             return;
13726         }
13727         
13728         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13729         
13730         if(this.hiddenName){
13731             
13732             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13733             
13734             this.hiddenField.dom.value =
13735                 this.hiddenValue !== undefined ? this.hiddenValue :
13736                 this.value !== undefined ? this.value : '';
13737
13738             // prevent input submission
13739             this.el.dom.removeAttribute('name');
13740             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13741              
13742              
13743         }
13744         //if(Roo.isGecko){
13745         //    this.el.dom.setAttribute('autocomplete', 'off');
13746         //}
13747         
13748         var cls = 'x-combo-list';
13749         
13750         //this.list = new Roo.Layer({
13751         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13752         //});
13753         
13754         var _this = this;
13755         
13756         (function(){
13757             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13758             _this.list.setWidth(lw);
13759         }).defer(100);
13760         
13761         this.list.on('mouseover', this.onViewOver, this);
13762         this.list.on('mousemove', this.onViewMove, this);
13763         this.list.on('scroll', this.onViewScroll, this);
13764         
13765         /*
13766         this.list.swallowEvent('mousewheel');
13767         this.assetHeight = 0;
13768
13769         if(this.title){
13770             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13771             this.assetHeight += this.header.getHeight();
13772         }
13773
13774         this.innerList = this.list.createChild({cls:cls+'-inner'});
13775         this.innerList.on('mouseover', this.onViewOver, this);
13776         this.innerList.on('mousemove', this.onViewMove, this);
13777         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13778         
13779         if(this.allowBlank && !this.pageSize && !this.disableClear){
13780             this.footer = this.list.createChild({cls:cls+'-ft'});
13781             this.pageTb = new Roo.Toolbar(this.footer);
13782            
13783         }
13784         if(this.pageSize){
13785             this.footer = this.list.createChild({cls:cls+'-ft'});
13786             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13787                     {pageSize: this.pageSize});
13788             
13789         }
13790         
13791         if (this.pageTb && this.allowBlank && !this.disableClear) {
13792             var _this = this;
13793             this.pageTb.add(new Roo.Toolbar.Fill(), {
13794                 cls: 'x-btn-icon x-btn-clear',
13795                 text: '&#160;',
13796                 handler: function()
13797                 {
13798                     _this.collapse();
13799                     _this.clearValue();
13800                     _this.onSelect(false, -1);
13801                 }
13802             });
13803         }
13804         if (this.footer) {
13805             this.assetHeight += this.footer.getHeight();
13806         }
13807         */
13808             
13809         if(!this.tpl){
13810             this.tpl = Roo.bootstrap.version == 4 ?
13811                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
13812                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
13813         }
13814
13815         this.view = new Roo.View(this.list, this.tpl, {
13816             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13817         });
13818         //this.view.wrapEl.setDisplayed(false);
13819         this.view.on('click', this.onViewClick, this);
13820         
13821         
13822         this.store.on('beforeload', this.onBeforeLoad, this);
13823         this.store.on('load', this.onLoad, this);
13824         this.store.on('loadexception', this.onLoadException, this);
13825         /*
13826         if(this.resizable){
13827             this.resizer = new Roo.Resizable(this.list,  {
13828                pinned:true, handles:'se'
13829             });
13830             this.resizer.on('resize', function(r, w, h){
13831                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13832                 this.listWidth = w;
13833                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13834                 this.restrictHeight();
13835             }, this);
13836             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13837         }
13838         */
13839         if(!this.editable){
13840             this.editable = true;
13841             this.setEditable(false);
13842         }
13843         
13844         /*
13845         
13846         if (typeof(this.events.add.listeners) != 'undefined') {
13847             
13848             this.addicon = this.wrap.createChild(
13849                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13850        
13851             this.addicon.on('click', function(e) {
13852                 this.fireEvent('add', this);
13853             }, this);
13854         }
13855         if (typeof(this.events.edit.listeners) != 'undefined') {
13856             
13857             this.editicon = this.wrap.createChild(
13858                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13859             if (this.addicon) {
13860                 this.editicon.setStyle('margin-left', '40px');
13861             }
13862             this.editicon.on('click', function(e) {
13863                 
13864                 // we fire even  if inothing is selected..
13865                 this.fireEvent('edit', this, this.lastData );
13866                 
13867             }, this);
13868         }
13869         */
13870         
13871         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13872             "up" : function(e){
13873                 this.inKeyMode = true;
13874                 this.selectPrev();
13875             },
13876
13877             "down" : function(e){
13878                 if(!this.isExpanded()){
13879                     this.onTriggerClick();
13880                 }else{
13881                     this.inKeyMode = true;
13882                     this.selectNext();
13883                 }
13884             },
13885
13886             "enter" : function(e){
13887 //                this.onViewClick();
13888                 //return true;
13889                 this.collapse();
13890                 
13891                 if(this.fireEvent("specialkey", this, e)){
13892                     this.onViewClick(false);
13893                 }
13894                 
13895                 return true;
13896             },
13897
13898             "esc" : function(e){
13899                 this.collapse();
13900             },
13901
13902             "tab" : function(e){
13903                 this.collapse();
13904                 
13905                 if(this.fireEvent("specialkey", this, e)){
13906                     this.onViewClick(false);
13907                 }
13908                 
13909                 return true;
13910             },
13911
13912             scope : this,
13913
13914             doRelay : function(foo, bar, hname){
13915                 if(hname == 'down' || this.scope.isExpanded()){
13916                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13917                 }
13918                 return true;
13919             },
13920
13921             forceKeyDown: true
13922         });
13923         
13924         
13925         this.queryDelay = Math.max(this.queryDelay || 10,
13926                 this.mode == 'local' ? 10 : 250);
13927         
13928         
13929         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13930         
13931         if(this.typeAhead){
13932             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13933         }
13934         if(this.editable !== false){
13935             this.inputEl().on("keyup", this.onKeyUp, this);
13936         }
13937         if(this.forceSelection){
13938             this.inputEl().on('blur', this.doForce, this);
13939         }
13940         
13941         if(this.multiple){
13942             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13943             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13944         }
13945     },
13946     
13947     initTickableEvents: function()
13948     {   
13949         this.createList();
13950         
13951         if(this.hiddenName){
13952             
13953             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13954             
13955             this.hiddenField.dom.value =
13956                 this.hiddenValue !== undefined ? this.hiddenValue :
13957                 this.value !== undefined ? this.value : '';
13958
13959             // prevent input submission
13960             this.el.dom.removeAttribute('name');
13961             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13962              
13963              
13964         }
13965         
13966 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13967         
13968         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13969         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13970         if(this.triggerList){
13971             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13972         }
13973          
13974         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13975         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13976         
13977         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13978         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13979         
13980         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13981         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13982         
13983         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13984         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13985         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13986         
13987         this.okBtn.hide();
13988         this.cancelBtn.hide();
13989         
13990         var _this = this;
13991         
13992         (function(){
13993             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13994             _this.list.setWidth(lw);
13995         }).defer(100);
13996         
13997         this.list.on('mouseover', this.onViewOver, this);
13998         this.list.on('mousemove', this.onViewMove, this);
13999         
14000         this.list.on('scroll', this.onViewScroll, this);
14001         
14002         if(!this.tpl){
14003             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
14004                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
14005         }
14006
14007         this.view = new Roo.View(this.list, this.tpl, {
14008             singleSelect:true,
14009             tickable:true,
14010             parent:this,
14011             store: this.store,
14012             selectedClass: this.selectedClass
14013         });
14014         
14015         //this.view.wrapEl.setDisplayed(false);
14016         this.view.on('click', this.onViewClick, this);
14017         
14018         
14019         
14020         this.store.on('beforeload', this.onBeforeLoad, this);
14021         this.store.on('load', this.onLoad, this);
14022         this.store.on('loadexception', this.onLoadException, this);
14023         
14024         if(this.editable){
14025             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
14026                 "up" : function(e){
14027                     this.inKeyMode = true;
14028                     this.selectPrev();
14029                 },
14030
14031                 "down" : function(e){
14032                     this.inKeyMode = true;
14033                     this.selectNext();
14034                 },
14035
14036                 "enter" : function(e){
14037                     if(this.fireEvent("specialkey", this, e)){
14038                         this.onViewClick(false);
14039                     }
14040                     
14041                     return true;
14042                 },
14043
14044                 "esc" : function(e){
14045                     this.onTickableFooterButtonClick(e, false, false);
14046                 },
14047
14048                 "tab" : function(e){
14049                     this.fireEvent("specialkey", this, e);
14050                     
14051                     this.onTickableFooterButtonClick(e, false, false);
14052                     
14053                     return true;
14054                 },
14055
14056                 scope : this,
14057
14058                 doRelay : function(e, fn, key){
14059                     if(this.scope.isExpanded()){
14060                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
14061                     }
14062                     return true;
14063                 },
14064
14065                 forceKeyDown: true
14066             });
14067         }
14068         
14069         this.queryDelay = Math.max(this.queryDelay || 10,
14070                 this.mode == 'local' ? 10 : 250);
14071         
14072         
14073         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
14074         
14075         if(this.typeAhead){
14076             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
14077         }
14078         
14079         if(this.editable !== false){
14080             this.tickableInputEl().on("keyup", this.onKeyUp, this);
14081         }
14082         
14083         this.indicator = this.indicatorEl();
14084         
14085         if(this.indicator){
14086             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
14087             this.indicator.hide();
14088         }
14089         
14090     },
14091
14092     onDestroy : function(){
14093         if(this.view){
14094             this.view.setStore(null);
14095             this.view.el.removeAllListeners();
14096             this.view.el.remove();
14097             this.view.purgeListeners();
14098         }
14099         if(this.list){
14100             this.list.dom.innerHTML  = '';
14101         }
14102         
14103         if(this.store){
14104             this.store.un('beforeload', this.onBeforeLoad, this);
14105             this.store.un('load', this.onLoad, this);
14106             this.store.un('loadexception', this.onLoadException, this);
14107         }
14108         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
14109     },
14110
14111     // private
14112     fireKey : function(e){
14113         if(e.isNavKeyPress() && !this.list.isVisible()){
14114             this.fireEvent("specialkey", this, e);
14115         }
14116     },
14117
14118     // private
14119     onResize: function(w, h){
14120 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
14121 //        
14122 //        if(typeof w != 'number'){
14123 //            // we do not handle it!?!?
14124 //            return;
14125 //        }
14126 //        var tw = this.trigger.getWidth();
14127 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
14128 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
14129 //        var x = w - tw;
14130 //        this.inputEl().setWidth( this.adjustWidth('input', x));
14131 //            
14132 //        //this.trigger.setStyle('left', x+'px');
14133 //        
14134 //        if(this.list && this.listWidth === undefined){
14135 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
14136 //            this.list.setWidth(lw);
14137 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
14138 //        }
14139         
14140     
14141         
14142     },
14143
14144     /**
14145      * Allow or prevent the user from directly editing the field text.  If false is passed,
14146      * the user will only be able to select from the items defined in the dropdown list.  This method
14147      * is the runtime equivalent of setting the 'editable' config option at config time.
14148      * @param {Boolean} value True to allow the user to directly edit the field text
14149      */
14150     setEditable : function(value){
14151         if(value == this.editable){
14152             return;
14153         }
14154         this.editable = value;
14155         if(!value){
14156             this.inputEl().dom.setAttribute('readOnly', true);
14157             this.inputEl().on('mousedown', this.onTriggerClick,  this);
14158             this.inputEl().addClass('x-combo-noedit');
14159         }else{
14160             this.inputEl().dom.setAttribute('readOnly', false);
14161             this.inputEl().un('mousedown', this.onTriggerClick,  this);
14162             this.inputEl().removeClass('x-combo-noedit');
14163         }
14164     },
14165
14166     // private
14167     
14168     onBeforeLoad : function(combo,opts){
14169         if(!this.hasFocus){
14170             return;
14171         }
14172          if (!opts.add) {
14173             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
14174          }
14175         this.restrictHeight();
14176         this.selectedIndex = -1;
14177     },
14178
14179     // private
14180     onLoad : function(){
14181         
14182         this.hasQuery = false;
14183         
14184         if(!this.hasFocus){
14185             return;
14186         }
14187         
14188         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14189             this.loading.hide();
14190         }
14191         
14192         if(this.store.getCount() > 0){
14193             
14194             this.expand();
14195             this.restrictHeight();
14196             if(this.lastQuery == this.allQuery){
14197                 if(this.editable && !this.tickable){
14198                     this.inputEl().dom.select();
14199                 }
14200                 
14201                 if(
14202                     !this.selectByValue(this.value, true) &&
14203                     this.autoFocus && 
14204                     (
14205                         !this.store.lastOptions ||
14206                         typeof(this.store.lastOptions.add) == 'undefined' || 
14207                         this.store.lastOptions.add != true
14208                     )
14209                 ){
14210                     this.select(0, true);
14211                 }
14212             }else{
14213                 if(this.autoFocus){
14214                     this.selectNext();
14215                 }
14216                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
14217                     this.taTask.delay(this.typeAheadDelay);
14218                 }
14219             }
14220         }else{
14221             this.onEmptyResults();
14222         }
14223         
14224         //this.el.focus();
14225     },
14226     // private
14227     onLoadException : function()
14228     {
14229         this.hasQuery = false;
14230         
14231         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
14232             this.loading.hide();
14233         }
14234         
14235         if(this.tickable && this.editable){
14236             return;
14237         }
14238         
14239         this.collapse();
14240         // only causes errors at present
14241         //Roo.log(this.store.reader.jsonData);
14242         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
14243             // fixme
14244             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
14245         //}
14246         
14247         
14248     },
14249     // private
14250     onTypeAhead : function(){
14251         if(this.store.getCount() > 0){
14252             var r = this.store.getAt(0);
14253             var newValue = r.data[this.displayField];
14254             var len = newValue.length;
14255             var selStart = this.getRawValue().length;
14256             
14257             if(selStart != len){
14258                 this.setRawValue(newValue);
14259                 this.selectText(selStart, newValue.length);
14260             }
14261         }
14262     },
14263
14264     // private
14265     onSelect : function(record, index){
14266         
14267         if(this.fireEvent('beforeselect', this, record, index) !== false){
14268         
14269             this.setFromData(index > -1 ? record.data : false);
14270             
14271             this.collapse();
14272             this.fireEvent('select', this, record, index);
14273         }
14274     },
14275
14276     /**
14277      * Returns the currently selected field value or empty string if no value is set.
14278      * @return {String} value The selected value
14279      */
14280     getValue : function()
14281     {
14282         if(Roo.isIOS && this.useNativeIOS){
14283             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
14284         }
14285         
14286         if(this.multiple){
14287             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
14288         }
14289         
14290         if(this.valueField){
14291             return typeof this.value != 'undefined' ? this.value : '';
14292         }else{
14293             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
14294         }
14295     },
14296     
14297     getRawValue : function()
14298     {
14299         if(Roo.isIOS && this.useNativeIOS){
14300             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
14301         }
14302         
14303         var v = this.inputEl().getValue();
14304         
14305         return v;
14306     },
14307
14308     /**
14309      * Clears any text/value currently set in the field
14310      */
14311     clearValue : function(){
14312         
14313         if(this.hiddenField){
14314             this.hiddenField.dom.value = '';
14315         }
14316         this.value = '';
14317         this.setRawValue('');
14318         this.lastSelectionText = '';
14319         this.lastData = false;
14320         
14321         var close = this.closeTriggerEl();
14322         
14323         if(close){
14324             close.hide();
14325         }
14326         
14327         this.validate();
14328         
14329     },
14330
14331     /**
14332      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
14333      * will be displayed in the field.  If the value does not match the data value of an existing item,
14334      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
14335      * Otherwise the field will be blank (although the value will still be set).
14336      * @param {String} value The value to match
14337      */
14338     setValue : function(v)
14339     {
14340         if(Roo.isIOS && this.useNativeIOS){
14341             this.setIOSValue(v);
14342             return;
14343         }
14344         
14345         if(this.multiple){
14346             this.syncValue();
14347             return;
14348         }
14349         
14350         var text = v;
14351         if(this.valueField){
14352             var r = this.findRecord(this.valueField, v);
14353             if(r){
14354                 text = r.data[this.displayField];
14355             }else if(this.valueNotFoundText !== undefined){
14356                 text = this.valueNotFoundText;
14357             }
14358         }
14359         this.lastSelectionText = text;
14360         if(this.hiddenField){
14361             this.hiddenField.dom.value = v;
14362         }
14363         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
14364         this.value = v;
14365         
14366         var close = this.closeTriggerEl();
14367         
14368         if(close){
14369             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
14370         }
14371         
14372         this.validate();
14373     },
14374     /**
14375      * @property {Object} the last set data for the element
14376      */
14377     
14378     lastData : false,
14379     /**
14380      * Sets the value of the field based on a object which is related to the record format for the store.
14381      * @param {Object} value the value to set as. or false on reset?
14382      */
14383     setFromData : function(o){
14384         
14385         if(this.multiple){
14386             this.addItem(o);
14387             return;
14388         }
14389             
14390         var dv = ''; // display value
14391         var vv = ''; // value value..
14392         this.lastData = o;
14393         if (this.displayField) {
14394             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14395         } else {
14396             // this is an error condition!!!
14397             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14398         }
14399         
14400         if(this.valueField){
14401             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
14402         }
14403         
14404         var close = this.closeTriggerEl();
14405         
14406         if(close){
14407             if(dv.length || vv * 1 > 0){
14408                 close.show() ;
14409                 this.blockFocus=true;
14410             } else {
14411                 close.hide();
14412             }             
14413         }
14414         
14415         if(this.hiddenField){
14416             this.hiddenField.dom.value = vv;
14417             
14418             this.lastSelectionText = dv;
14419             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14420             this.value = vv;
14421             return;
14422         }
14423         // no hidden field.. - we store the value in 'value', but still display
14424         // display field!!!!
14425         this.lastSelectionText = dv;
14426         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
14427         this.value = vv;
14428         
14429         
14430         
14431     },
14432     // private
14433     reset : function(){
14434         // overridden so that last data is reset..
14435         
14436         if(this.multiple){
14437             this.clearItem();
14438             return;
14439         }
14440         
14441         this.setValue(this.originalValue);
14442         //this.clearInvalid();
14443         this.lastData = false;
14444         if (this.view) {
14445             this.view.clearSelections();
14446         }
14447         
14448         this.validate();
14449     },
14450     // private
14451     findRecord : function(prop, value){
14452         var record;
14453         if(this.store.getCount() > 0){
14454             this.store.each(function(r){
14455                 if(r.data[prop] == value){
14456                     record = r;
14457                     return false;
14458                 }
14459                 return true;
14460             });
14461         }
14462         return record;
14463     },
14464     
14465     getName: function()
14466     {
14467         // returns hidden if it's set..
14468         if (!this.rendered) {return ''};
14469         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
14470         
14471     },
14472     // private
14473     onViewMove : function(e, t){
14474         this.inKeyMode = false;
14475     },
14476
14477     // private
14478     onViewOver : function(e, t){
14479         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
14480             return;
14481         }
14482         var item = this.view.findItemFromChild(t);
14483         
14484         if(item){
14485             var index = this.view.indexOf(item);
14486             this.select(index, false);
14487         }
14488     },
14489
14490     // private
14491     onViewClick : function(view, doFocus, el, e)
14492     {
14493         var index = this.view.getSelectedIndexes()[0];
14494         
14495         var r = this.store.getAt(index);
14496         
14497         if(this.tickable){
14498             
14499             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
14500                 return;
14501             }
14502             
14503             var rm = false;
14504             var _this = this;
14505             
14506             Roo.each(this.tickItems, function(v,k){
14507                 
14508                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
14509                     Roo.log(v);
14510                     _this.tickItems.splice(k, 1);
14511                     
14512                     if(typeof(e) == 'undefined' && view == false){
14513                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
14514                     }
14515                     
14516                     rm = true;
14517                     return;
14518                 }
14519             });
14520             
14521             if(rm){
14522                 return;
14523             }
14524             
14525             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
14526                 this.tickItems.push(r.data);
14527             }
14528             
14529             if(typeof(e) == 'undefined' && view == false){
14530                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
14531             }
14532                     
14533             return;
14534         }
14535         
14536         if(r){
14537             this.onSelect(r, index);
14538         }
14539         if(doFocus !== false && !this.blockFocus){
14540             this.inputEl().focus();
14541         }
14542     },
14543
14544     // private
14545     restrictHeight : function(){
14546         //this.innerList.dom.style.height = '';
14547         //var inner = this.innerList.dom;
14548         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
14549         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
14550         //this.list.beginUpdate();
14551         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
14552         this.list.alignTo(this.inputEl(), this.listAlign);
14553         this.list.alignTo(this.inputEl(), this.listAlign);
14554         //this.list.endUpdate();
14555     },
14556
14557     // private
14558     onEmptyResults : function(){
14559         
14560         if(this.tickable && this.editable){
14561             this.hasFocus = false;
14562             this.restrictHeight();
14563             return;
14564         }
14565         
14566         this.collapse();
14567     },
14568
14569     /**
14570      * Returns true if the dropdown list is expanded, else false.
14571      */
14572     isExpanded : function(){
14573         return this.list.isVisible();
14574     },
14575
14576     /**
14577      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14578      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14579      * @param {String} value The data value of the item to select
14580      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14581      * selected item if it is not currently in view (defaults to true)
14582      * @return {Boolean} True if the value matched an item in the list, else false
14583      */
14584     selectByValue : function(v, scrollIntoView){
14585         if(v !== undefined && v !== null){
14586             var r = this.findRecord(this.valueField || this.displayField, v);
14587             if(r){
14588                 this.select(this.store.indexOf(r), scrollIntoView);
14589                 return true;
14590             }
14591         }
14592         return false;
14593     },
14594
14595     /**
14596      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14597      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14598      * @param {Number} index The zero-based index of the list item to select
14599      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14600      * selected item if it is not currently in view (defaults to true)
14601      */
14602     select : function(index, scrollIntoView){
14603         this.selectedIndex = index;
14604         this.view.select(index);
14605         if(scrollIntoView !== false){
14606             var el = this.view.getNode(index);
14607             /*
14608              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14609              */
14610             if(el){
14611                 this.list.scrollChildIntoView(el, false);
14612             }
14613         }
14614     },
14615
14616     // private
14617     selectNext : function(){
14618         var ct = this.store.getCount();
14619         if(ct > 0){
14620             if(this.selectedIndex == -1){
14621                 this.select(0);
14622             }else if(this.selectedIndex < ct-1){
14623                 this.select(this.selectedIndex+1);
14624             }
14625         }
14626     },
14627
14628     // private
14629     selectPrev : function(){
14630         var ct = this.store.getCount();
14631         if(ct > 0){
14632             if(this.selectedIndex == -1){
14633                 this.select(0);
14634             }else if(this.selectedIndex != 0){
14635                 this.select(this.selectedIndex-1);
14636             }
14637         }
14638     },
14639
14640     // private
14641     onKeyUp : function(e){
14642         if(this.editable !== false && !e.isSpecialKey()){
14643             this.lastKey = e.getKey();
14644             this.dqTask.delay(this.queryDelay);
14645         }
14646     },
14647
14648     // private
14649     validateBlur : function(){
14650         return !this.list || !this.list.isVisible();   
14651     },
14652
14653     // private
14654     initQuery : function(){
14655         
14656         var v = this.getRawValue();
14657         
14658         if(this.tickable && this.editable){
14659             v = this.tickableInputEl().getValue();
14660         }
14661         
14662         this.doQuery(v);
14663     },
14664
14665     // private
14666     doForce : function(){
14667         if(this.inputEl().dom.value.length > 0){
14668             this.inputEl().dom.value =
14669                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14670              
14671         }
14672     },
14673
14674     /**
14675      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14676      * query allowing the query action to be canceled if needed.
14677      * @param {String} query The SQL query to execute
14678      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14679      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14680      * saved in the current store (defaults to false)
14681      */
14682     doQuery : function(q, forceAll){
14683         
14684         if(q === undefined || q === null){
14685             q = '';
14686         }
14687         var qe = {
14688             query: q,
14689             forceAll: forceAll,
14690             combo: this,
14691             cancel:false
14692         };
14693         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14694             return false;
14695         }
14696         q = qe.query;
14697         
14698         forceAll = qe.forceAll;
14699         if(forceAll === true || (q.length >= this.minChars)){
14700             
14701             this.hasQuery = true;
14702             
14703             if(this.lastQuery != q || this.alwaysQuery){
14704                 this.lastQuery = q;
14705                 if(this.mode == 'local'){
14706                     this.selectedIndex = -1;
14707                     if(forceAll){
14708                         this.store.clearFilter();
14709                     }else{
14710                         
14711                         if(this.specialFilter){
14712                             this.fireEvent('specialfilter', this);
14713                             this.onLoad();
14714                             return;
14715                         }
14716                         
14717                         this.store.filter(this.displayField, q);
14718                     }
14719                     
14720                     this.store.fireEvent("datachanged", this.store);
14721                     
14722                     this.onLoad();
14723                     
14724                     
14725                 }else{
14726                     
14727                     this.store.baseParams[this.queryParam] = q;
14728                     
14729                     var options = {params : this.getParams(q)};
14730                     
14731                     if(this.loadNext){
14732                         options.add = true;
14733                         options.params.start = this.page * this.pageSize;
14734                     }
14735                     
14736                     this.store.load(options);
14737                     
14738                     /*
14739                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14740                      *  we should expand the list on onLoad
14741                      *  so command out it
14742                      */
14743 //                    this.expand();
14744                 }
14745             }else{
14746                 this.selectedIndex = -1;
14747                 this.onLoad();   
14748             }
14749         }
14750         
14751         this.loadNext = false;
14752     },
14753     
14754     // private
14755     getParams : function(q){
14756         var p = {};
14757         //p[this.queryParam] = q;
14758         
14759         if(this.pageSize){
14760             p.start = 0;
14761             p.limit = this.pageSize;
14762         }
14763         return p;
14764     },
14765
14766     /**
14767      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14768      */
14769     collapse : function(){
14770         if(!this.isExpanded()){
14771             return;
14772         }
14773         
14774         this.list.hide();
14775         
14776         this.hasFocus = false;
14777         
14778         if(this.tickable){
14779             this.okBtn.hide();
14780             this.cancelBtn.hide();
14781             this.trigger.show();
14782             
14783             if(this.editable){
14784                 this.tickableInputEl().dom.value = '';
14785                 this.tickableInputEl().blur();
14786             }
14787             
14788         }
14789         
14790         Roo.get(document).un('mousedown', this.collapseIf, this);
14791         Roo.get(document).un('mousewheel', this.collapseIf, this);
14792         if (!this.editable) {
14793             Roo.get(document).un('keydown', this.listKeyPress, this);
14794         }
14795         this.fireEvent('collapse', this);
14796         
14797         this.validate();
14798     },
14799
14800     // private
14801     collapseIf : function(e){
14802         var in_combo  = e.within(this.el);
14803         var in_list =  e.within(this.list);
14804         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14805         
14806         if (in_combo || in_list || is_list) {
14807             //e.stopPropagation();
14808             return;
14809         }
14810         
14811         if(this.tickable){
14812             this.onTickableFooterButtonClick(e, false, false);
14813         }
14814
14815         this.collapse();
14816         
14817     },
14818
14819     /**
14820      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14821      */
14822     expand : function(){
14823        
14824         if(this.isExpanded() || !this.hasFocus){
14825             return;
14826         }
14827         
14828         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14829         this.list.setWidth(lw);
14830         
14831         Roo.log('expand');
14832         
14833         this.list.show();
14834         
14835         this.restrictHeight();
14836         
14837         if(this.tickable){
14838             
14839             this.tickItems = Roo.apply([], this.item);
14840             
14841             this.okBtn.show();
14842             this.cancelBtn.show();
14843             this.trigger.hide();
14844             
14845             if(this.editable){
14846                 this.tickableInputEl().focus();
14847             }
14848             
14849         }
14850         
14851         Roo.get(document).on('mousedown', this.collapseIf, this);
14852         Roo.get(document).on('mousewheel', this.collapseIf, this);
14853         if (!this.editable) {
14854             Roo.get(document).on('keydown', this.listKeyPress, this);
14855         }
14856         
14857         this.fireEvent('expand', this);
14858     },
14859
14860     // private
14861     // Implements the default empty TriggerField.onTriggerClick function
14862     onTriggerClick : function(e)
14863     {
14864         Roo.log('trigger click');
14865         
14866         if(this.disabled || !this.triggerList){
14867             return;
14868         }
14869         
14870         this.page = 0;
14871         this.loadNext = false;
14872         
14873         if(this.isExpanded()){
14874             this.collapse();
14875             if (!this.blockFocus) {
14876                 this.inputEl().focus();
14877             }
14878             
14879         }else {
14880             this.hasFocus = true;
14881             if(this.triggerAction == 'all') {
14882                 this.doQuery(this.allQuery, true);
14883             } else {
14884                 this.doQuery(this.getRawValue());
14885             }
14886             if (!this.blockFocus) {
14887                 this.inputEl().focus();
14888             }
14889         }
14890     },
14891     
14892     onTickableTriggerClick : function(e)
14893     {
14894         if(this.disabled){
14895             return;
14896         }
14897         
14898         this.page = 0;
14899         this.loadNext = false;
14900         this.hasFocus = true;
14901         
14902         if(this.triggerAction == 'all') {
14903             this.doQuery(this.allQuery, true);
14904         } else {
14905             this.doQuery(this.getRawValue());
14906         }
14907     },
14908     
14909     onSearchFieldClick : function(e)
14910     {
14911         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14912             this.onTickableFooterButtonClick(e, false, false);
14913             return;
14914         }
14915         
14916         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14917             return;
14918         }
14919         
14920         this.page = 0;
14921         this.loadNext = false;
14922         this.hasFocus = true;
14923         
14924         if(this.triggerAction == 'all') {
14925             this.doQuery(this.allQuery, true);
14926         } else {
14927             this.doQuery(this.getRawValue());
14928         }
14929     },
14930     
14931     listKeyPress : function(e)
14932     {
14933         //Roo.log('listkeypress');
14934         // scroll to first matching element based on key pres..
14935         if (e.isSpecialKey()) {
14936             return false;
14937         }
14938         var k = String.fromCharCode(e.getKey()).toUpperCase();
14939         //Roo.log(k);
14940         var match  = false;
14941         var csel = this.view.getSelectedNodes();
14942         var cselitem = false;
14943         if (csel.length) {
14944             var ix = this.view.indexOf(csel[0]);
14945             cselitem  = this.store.getAt(ix);
14946             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14947                 cselitem = false;
14948             }
14949             
14950         }
14951         
14952         this.store.each(function(v) { 
14953             if (cselitem) {
14954                 // start at existing selection.
14955                 if (cselitem.id == v.id) {
14956                     cselitem = false;
14957                 }
14958                 return true;
14959             }
14960                 
14961             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14962                 match = this.store.indexOf(v);
14963                 return false;
14964             }
14965             return true;
14966         }, this);
14967         
14968         if (match === false) {
14969             return true; // no more action?
14970         }
14971         // scroll to?
14972         this.view.select(match);
14973         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14974         sn.scrollIntoView(sn.dom.parentNode, false);
14975     },
14976     
14977     onViewScroll : function(e, t){
14978         
14979         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){
14980             return;
14981         }
14982         
14983         this.hasQuery = true;
14984         
14985         this.loading = this.list.select('.loading', true).first();
14986         
14987         if(this.loading === null){
14988             this.list.createChild({
14989                 tag: 'div',
14990                 cls: 'loading roo-select2-more-results roo-select2-active',
14991                 html: 'Loading more results...'
14992             });
14993             
14994             this.loading = this.list.select('.loading', true).first();
14995             
14996             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14997             
14998             this.loading.hide();
14999         }
15000         
15001         this.loading.show();
15002         
15003         var _combo = this;
15004         
15005         this.page++;
15006         this.loadNext = true;
15007         
15008         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
15009         
15010         return;
15011     },
15012     
15013     addItem : function(o)
15014     {   
15015         var dv = ''; // display value
15016         
15017         if (this.displayField) {
15018             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
15019         } else {
15020             // this is an error condition!!!
15021             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
15022         }
15023         
15024         if(!dv.length){
15025             return;
15026         }
15027         
15028         var choice = this.choices.createChild({
15029             tag: 'li',
15030             cls: 'roo-select2-search-choice',
15031             cn: [
15032                 {
15033                     tag: 'div',
15034                     html: dv
15035                 },
15036                 {
15037                     tag: 'a',
15038                     href: '#',
15039                     cls: 'roo-select2-search-choice-close fa fa-times',
15040                     tabindex: '-1'
15041                 }
15042             ]
15043             
15044         }, this.searchField);
15045         
15046         var close = choice.select('a.roo-select2-search-choice-close', true).first();
15047         
15048         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
15049         
15050         this.item.push(o);
15051         
15052         this.lastData = o;
15053         
15054         this.syncValue();
15055         
15056         this.inputEl().dom.value = '';
15057         
15058         this.validate();
15059     },
15060     
15061     onRemoveItem : function(e, _self, o)
15062     {
15063         e.preventDefault();
15064         
15065         this.lastItem = Roo.apply([], this.item);
15066         
15067         var index = this.item.indexOf(o.data) * 1;
15068         
15069         if( index < 0){
15070             Roo.log('not this item?!');
15071             return;
15072         }
15073         
15074         this.item.splice(index, 1);
15075         o.item.remove();
15076         
15077         this.syncValue();
15078         
15079         this.fireEvent('remove', this, e);
15080         
15081         this.validate();
15082         
15083     },
15084     
15085     syncValue : function()
15086     {
15087         if(!this.item.length){
15088             this.clearValue();
15089             return;
15090         }
15091             
15092         var value = [];
15093         var _this = this;
15094         Roo.each(this.item, function(i){
15095             if(_this.valueField){
15096                 value.push(i[_this.valueField]);
15097                 return;
15098             }
15099
15100             value.push(i);
15101         });
15102
15103         this.value = value.join(',');
15104
15105         if(this.hiddenField){
15106             this.hiddenField.dom.value = this.value;
15107         }
15108         
15109         this.store.fireEvent("datachanged", this.store);
15110         
15111         this.validate();
15112     },
15113     
15114     clearItem : function()
15115     {
15116         if(!this.multiple){
15117             return;
15118         }
15119         
15120         this.item = [];
15121         
15122         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
15123            c.remove();
15124         });
15125         
15126         this.syncValue();
15127         
15128         this.validate();
15129         
15130         if(this.tickable && !Roo.isTouch){
15131             this.view.refresh();
15132         }
15133     },
15134     
15135     inputEl: function ()
15136     {
15137         if(Roo.isIOS && this.useNativeIOS){
15138             return this.el.select('select.roo-ios-select', true).first();
15139         }
15140         
15141         if(Roo.isTouch && this.mobileTouchView){
15142             return this.el.select('input.form-control',true).first();
15143         }
15144         
15145         if(this.tickable){
15146             return this.searchField;
15147         }
15148         
15149         return this.el.select('input.form-control',true).first();
15150     },
15151     
15152     onTickableFooterButtonClick : function(e, btn, el)
15153     {
15154         e.preventDefault();
15155         
15156         this.lastItem = Roo.apply([], this.item);
15157         
15158         if(btn && btn.name == 'cancel'){
15159             this.tickItems = Roo.apply([], this.item);
15160             this.collapse();
15161             return;
15162         }
15163         
15164         this.clearItem();
15165         
15166         var _this = this;
15167         
15168         Roo.each(this.tickItems, function(o){
15169             _this.addItem(o);
15170         });
15171         
15172         this.collapse();
15173         
15174     },
15175     
15176     validate : function()
15177     {
15178         if(this.getVisibilityEl().hasClass('hidden')){
15179             return true;
15180         }
15181         
15182         var v = this.getRawValue();
15183         
15184         if(this.multiple){
15185             v = this.getValue();
15186         }
15187         
15188         if(this.disabled || this.allowBlank || v.length){
15189             this.markValid();
15190             return true;
15191         }
15192         
15193         this.markInvalid();
15194         return false;
15195     },
15196     
15197     tickableInputEl : function()
15198     {
15199         if(!this.tickable || !this.editable){
15200             return this.inputEl();
15201         }
15202         
15203         return this.inputEl().select('.roo-select2-search-field-input', true).first();
15204     },
15205     
15206     
15207     getAutoCreateTouchView : function()
15208     {
15209         var id = Roo.id();
15210         
15211         var cfg = {
15212             cls: 'form-group' //input-group
15213         };
15214         
15215         var input =  {
15216             tag: 'input',
15217             id : id,
15218             type : this.inputType,
15219             cls : 'form-control x-combo-noedit',
15220             autocomplete: 'new-password',
15221             placeholder : this.placeholder || '',
15222             readonly : true
15223         };
15224         
15225         if (this.name) {
15226             input.name = this.name;
15227         }
15228         
15229         if (this.size) {
15230             input.cls += ' input-' + this.size;
15231         }
15232         
15233         if (this.disabled) {
15234             input.disabled = true;
15235         }
15236         
15237         var inputblock = {
15238             cls : '',
15239             cn : [
15240                 input
15241             ]
15242         };
15243         
15244         if(this.before){
15245             inputblock.cls += ' input-group';
15246             
15247             inputblock.cn.unshift({
15248                 tag :'span',
15249                 cls : 'input-group-addon input-group-prepend input-group-text',
15250                 html : this.before
15251             });
15252         }
15253         
15254         if(this.removable && !this.multiple){
15255             inputblock.cls += ' roo-removable';
15256             
15257             inputblock.cn.push({
15258                 tag: 'button',
15259                 html : 'x',
15260                 cls : 'roo-combo-removable-btn close'
15261             });
15262         }
15263
15264         if(this.hasFeedback && !this.allowBlank){
15265             
15266             inputblock.cls += ' has-feedback';
15267             
15268             inputblock.cn.push({
15269                 tag: 'span',
15270                 cls: 'glyphicon form-control-feedback'
15271             });
15272             
15273         }
15274         
15275         if (this.after) {
15276             
15277             inputblock.cls += (this.before) ? '' : ' input-group';
15278             
15279             inputblock.cn.push({
15280                 tag :'span',
15281                 cls : 'input-group-addon input-group-append input-group-text',
15282                 html : this.after
15283             });
15284         }
15285
15286         
15287         var ibwrap = inputblock;
15288         
15289         if(this.multiple){
15290             ibwrap = {
15291                 tag: 'ul',
15292                 cls: 'roo-select2-choices',
15293                 cn:[
15294                     {
15295                         tag: 'li',
15296                         cls: 'roo-select2-search-field',
15297                         cn: [
15298
15299                             inputblock
15300                         ]
15301                     }
15302                 ]
15303             };
15304         
15305             
15306         }
15307         
15308         var combobox = {
15309             cls: 'roo-select2-container input-group roo-touchview-combobox ',
15310             cn: [
15311                 {
15312                     tag: 'input',
15313                     type : 'hidden',
15314                     cls: 'form-hidden-field'
15315                 },
15316                 ibwrap
15317             ]
15318         };
15319         
15320         if(!this.multiple && this.showToggleBtn){
15321             
15322             var caret = {
15323                         tag: 'span',
15324                         cls: 'caret'
15325             };
15326             
15327             if (this.caret != false) {
15328                 caret = {
15329                      tag: 'i',
15330                      cls: 'fa fa-' + this.caret
15331                 };
15332                 
15333             }
15334             
15335             combobox.cn.push({
15336                 tag :'span',
15337                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
15338                 cn : [
15339                     caret,
15340                     {
15341                         tag: 'span',
15342                         cls: 'combobox-clear',
15343                         cn  : [
15344                             {
15345                                 tag : 'i',
15346                                 cls: 'icon-remove'
15347                             }
15348                         ]
15349                     }
15350                 ]
15351
15352             })
15353         }
15354         
15355         if(this.multiple){
15356             combobox.cls += ' roo-select2-container-multi';
15357         }
15358         
15359         var align = this.labelAlign || this.parentLabelAlign();
15360         
15361         if (align ==='left' && this.fieldLabel.length) {
15362
15363             cfg.cn = [
15364                 {
15365                    tag : 'i',
15366                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15367                    tooltip : 'This field is required'
15368                 },
15369                 {
15370                     tag: 'label',
15371                     cls : 'control-label col-form-label',
15372                     html : this.fieldLabel
15373
15374                 },
15375                 {
15376                     cls : '', 
15377                     cn: [
15378                         combobox
15379                     ]
15380                 }
15381             ];
15382             
15383             var labelCfg = cfg.cn[1];
15384             var contentCfg = cfg.cn[2];
15385             
15386
15387             if(this.indicatorpos == 'right'){
15388                 cfg.cn = [
15389                     {
15390                         tag: 'label',
15391                         'for' :  id,
15392                         cls : 'control-label col-form-label',
15393                         cn : [
15394                             {
15395                                 tag : 'span',
15396                                 html : this.fieldLabel
15397                             },
15398                             {
15399                                 tag : 'i',
15400                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15401                                 tooltip : 'This field is required'
15402                             }
15403                         ]
15404                     },
15405                     {
15406                         cls : "",
15407                         cn: [
15408                             combobox
15409                         ]
15410                     }
15411
15412                 ];
15413                 
15414                 labelCfg = cfg.cn[0];
15415                 contentCfg = cfg.cn[1];
15416             }
15417             
15418            
15419             
15420             if(this.labelWidth > 12){
15421                 labelCfg.style = "width: " + this.labelWidth + 'px';
15422             }
15423             
15424             if(this.labelWidth < 13 && this.labelmd == 0){
15425                 this.labelmd = this.labelWidth;
15426             }
15427             
15428             if(this.labellg > 0){
15429                 labelCfg.cls += ' col-lg-' + this.labellg;
15430                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15431             }
15432             
15433             if(this.labelmd > 0){
15434                 labelCfg.cls += ' col-md-' + this.labelmd;
15435                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15436             }
15437             
15438             if(this.labelsm > 0){
15439                 labelCfg.cls += ' col-sm-' + this.labelsm;
15440                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15441             }
15442             
15443             if(this.labelxs > 0){
15444                 labelCfg.cls += ' col-xs-' + this.labelxs;
15445                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15446             }
15447                 
15448                 
15449         } else if ( this.fieldLabel.length) {
15450             cfg.cn = [
15451                 {
15452                    tag : 'i',
15453                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
15454                    tooltip : 'This field is required'
15455                 },
15456                 {
15457                     tag: 'label',
15458                     cls : 'control-label',
15459                     html : this.fieldLabel
15460
15461                 },
15462                 {
15463                     cls : '', 
15464                     cn: [
15465                         combobox
15466                     ]
15467                 }
15468             ];
15469             
15470             if(this.indicatorpos == 'right'){
15471                 cfg.cn = [
15472                     {
15473                         tag: 'label',
15474                         cls : 'control-label',
15475                         html : this.fieldLabel,
15476                         cn : [
15477                             {
15478                                tag : 'i',
15479                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
15480                                tooltip : 'This field is required'
15481                             }
15482                         ]
15483                     },
15484                     {
15485                         cls : '', 
15486                         cn: [
15487                             combobox
15488                         ]
15489                     }
15490                 ];
15491             }
15492         } else {
15493             cfg.cn = combobox;    
15494         }
15495         
15496         
15497         var settings = this;
15498         
15499         ['xs','sm','md','lg'].map(function(size){
15500             if (settings[size]) {
15501                 cfg.cls += ' col-' + size + '-' + settings[size];
15502             }
15503         });
15504         
15505         return cfg;
15506     },
15507     
15508     initTouchView : function()
15509     {
15510         this.renderTouchView();
15511         
15512         this.touchViewEl.on('scroll', function(){
15513             this.el.dom.scrollTop = 0;
15514         }, this);
15515         
15516         this.originalValue = this.getValue();
15517         
15518         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
15519         
15520         this.inputEl().on("click", this.showTouchView, this);
15521         if (this.triggerEl) {
15522             this.triggerEl.on("click", this.showTouchView, this);
15523         }
15524         
15525         
15526         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
15527         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
15528         
15529         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
15530         
15531         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
15532         this.store.on('load', this.onTouchViewLoad, this);
15533         this.store.on('loadexception', this.onTouchViewLoadException, this);
15534         
15535         if(this.hiddenName){
15536             
15537             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15538             
15539             this.hiddenField.dom.value =
15540                 this.hiddenValue !== undefined ? this.hiddenValue :
15541                 this.value !== undefined ? this.value : '';
15542         
15543             this.el.dom.removeAttribute('name');
15544             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15545         }
15546         
15547         if(this.multiple){
15548             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15549             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15550         }
15551         
15552         if(this.removable && !this.multiple){
15553             var close = this.closeTriggerEl();
15554             if(close){
15555                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
15556                 close.on('click', this.removeBtnClick, this, close);
15557             }
15558         }
15559         /*
15560          * fix the bug in Safari iOS8
15561          */
15562         this.inputEl().on("focus", function(e){
15563             document.activeElement.blur();
15564         }, this);
15565         
15566         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
15567         
15568         return;
15569         
15570         
15571     },
15572     
15573     renderTouchView : function()
15574     {
15575         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15576         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15577         
15578         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15579         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15580         
15581         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15582         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15583         this.touchViewBodyEl.setStyle('overflow', 'auto');
15584         
15585         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15586         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15587         
15588         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15589         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15590         
15591     },
15592     
15593     showTouchView : function()
15594     {
15595         if(this.disabled){
15596             return;
15597         }
15598         
15599         this.touchViewHeaderEl.hide();
15600
15601         if(this.modalTitle.length){
15602             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15603             this.touchViewHeaderEl.show();
15604         }
15605
15606         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15607         this.touchViewEl.show();
15608
15609         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15610         
15611         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15612         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15613
15614         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15615
15616         if(this.modalTitle.length){
15617             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15618         }
15619         
15620         this.touchViewBodyEl.setHeight(bodyHeight);
15621
15622         if(this.animate){
15623             var _this = this;
15624             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15625         }else{
15626             this.touchViewEl.addClass('in');
15627         }
15628         
15629         if(this._touchViewMask){
15630             Roo.get(document.body).addClass("x-body-masked");
15631             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
15632             this._touchViewMask.setStyle('z-index', 10000);
15633             this._touchViewMask.addClass('show');
15634         }
15635         
15636         this.doTouchViewQuery();
15637         
15638     },
15639     
15640     hideTouchView : function()
15641     {
15642         this.touchViewEl.removeClass('in');
15643
15644         if(this.animate){
15645             var _this = this;
15646             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15647         }else{
15648             this.touchViewEl.setStyle('display', 'none');
15649         }
15650         
15651         if(this._touchViewMask){
15652             this._touchViewMask.removeClass('show');
15653             Roo.get(document.body).removeClass("x-body-masked");
15654         }
15655     },
15656     
15657     setTouchViewValue : function()
15658     {
15659         if(this.multiple){
15660             this.clearItem();
15661         
15662             var _this = this;
15663
15664             Roo.each(this.tickItems, function(o){
15665                 this.addItem(o);
15666             }, this);
15667         }
15668         
15669         this.hideTouchView();
15670     },
15671     
15672     doTouchViewQuery : function()
15673     {
15674         var qe = {
15675             query: '',
15676             forceAll: true,
15677             combo: this,
15678             cancel:false
15679         };
15680         
15681         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15682             return false;
15683         }
15684         
15685         if(!this.alwaysQuery || this.mode == 'local'){
15686             this.onTouchViewLoad();
15687             return;
15688         }
15689         
15690         this.store.load();
15691     },
15692     
15693     onTouchViewBeforeLoad : function(combo,opts)
15694     {
15695         return;
15696     },
15697
15698     // private
15699     onTouchViewLoad : function()
15700     {
15701         if(this.store.getCount() < 1){
15702             this.onTouchViewEmptyResults();
15703             return;
15704         }
15705         
15706         this.clearTouchView();
15707         
15708         var rawValue = this.getRawValue();
15709         
15710         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15711         
15712         this.tickItems = [];
15713         
15714         this.store.data.each(function(d, rowIndex){
15715             var row = this.touchViewListGroup.createChild(template);
15716             
15717             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15718                 row.addClass(d.data.cls);
15719             }
15720             
15721             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15722                 var cfg = {
15723                     data : d.data,
15724                     html : d.data[this.displayField]
15725                 };
15726                 
15727                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15728                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15729                 }
15730             }
15731             row.removeClass('selected');
15732             if(!this.multiple && this.valueField &&
15733                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15734             {
15735                 // radio buttons..
15736                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15737                 row.addClass('selected');
15738             }
15739             
15740             if(this.multiple && this.valueField &&
15741                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15742             {
15743                 
15744                 // checkboxes...
15745                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15746                 this.tickItems.push(d.data);
15747             }
15748             
15749             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15750             
15751         }, this);
15752         
15753         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15754         
15755         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15756
15757         if(this.modalTitle.length){
15758             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15759         }
15760
15761         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
15762         
15763         if(this.mobile_restrict_height && listHeight < bodyHeight){
15764             this.touchViewBodyEl.setHeight(listHeight);
15765         }
15766         
15767         var _this = this;
15768         
15769         if(firstChecked && listHeight > bodyHeight){
15770             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15771         }
15772         
15773     },
15774     
15775     onTouchViewLoadException : function()
15776     {
15777         this.hideTouchView();
15778     },
15779     
15780     onTouchViewEmptyResults : function()
15781     {
15782         this.clearTouchView();
15783         
15784         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15785         
15786         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15787         
15788     },
15789     
15790     clearTouchView : function()
15791     {
15792         this.touchViewListGroup.dom.innerHTML = '';
15793     },
15794     
15795     onTouchViewClick : function(e, el, o)
15796     {
15797         e.preventDefault();
15798         
15799         var row = o.row;
15800         var rowIndex = o.rowIndex;
15801         
15802         var r = this.store.getAt(rowIndex);
15803         
15804         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15805             
15806             if(!this.multiple){
15807                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15808                     c.dom.removeAttribute('checked');
15809                 }, this);
15810
15811                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15812
15813                 this.setFromData(r.data);
15814
15815                 var close = this.closeTriggerEl();
15816
15817                 if(close){
15818                     close.show();
15819                 }
15820
15821                 this.hideTouchView();
15822
15823                 this.fireEvent('select', this, r, rowIndex);
15824
15825                 return;
15826             }
15827
15828             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15829                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15830                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15831                 return;
15832             }
15833
15834             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15835             this.addItem(r.data);
15836             this.tickItems.push(r.data);
15837         }
15838     },
15839     
15840     getAutoCreateNativeIOS : function()
15841     {
15842         var cfg = {
15843             cls: 'form-group' //input-group,
15844         };
15845         
15846         var combobox =  {
15847             tag: 'select',
15848             cls : 'roo-ios-select'
15849         };
15850         
15851         if (this.name) {
15852             combobox.name = this.name;
15853         }
15854         
15855         if (this.disabled) {
15856             combobox.disabled = true;
15857         }
15858         
15859         var settings = this;
15860         
15861         ['xs','sm','md','lg'].map(function(size){
15862             if (settings[size]) {
15863                 cfg.cls += ' col-' + size + '-' + settings[size];
15864             }
15865         });
15866         
15867         cfg.cn = combobox;
15868         
15869         return cfg;
15870         
15871     },
15872     
15873     initIOSView : function()
15874     {
15875         this.store.on('load', this.onIOSViewLoad, this);
15876         
15877         return;
15878     },
15879     
15880     onIOSViewLoad : function()
15881     {
15882         if(this.store.getCount() < 1){
15883             return;
15884         }
15885         
15886         this.clearIOSView();
15887         
15888         if(this.allowBlank) {
15889             
15890             var default_text = '-- SELECT --';
15891             
15892             if(this.placeholder.length){
15893                 default_text = this.placeholder;
15894             }
15895             
15896             if(this.emptyTitle.length){
15897                 default_text += ' - ' + this.emptyTitle + ' -';
15898             }
15899             
15900             var opt = this.inputEl().createChild({
15901                 tag: 'option',
15902                 value : 0,
15903                 html : default_text
15904             });
15905             
15906             var o = {};
15907             o[this.valueField] = 0;
15908             o[this.displayField] = default_text;
15909             
15910             this.ios_options.push({
15911                 data : o,
15912                 el : opt
15913             });
15914             
15915         }
15916         
15917         this.store.data.each(function(d, rowIndex){
15918             
15919             var html = '';
15920             
15921             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15922                 html = d.data[this.displayField];
15923             }
15924             
15925             var value = '';
15926             
15927             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15928                 value = d.data[this.valueField];
15929             }
15930             
15931             var option = {
15932                 tag: 'option',
15933                 value : value,
15934                 html : html
15935             };
15936             
15937             if(this.value == d.data[this.valueField]){
15938                 option['selected'] = true;
15939             }
15940             
15941             var opt = this.inputEl().createChild(option);
15942             
15943             this.ios_options.push({
15944                 data : d.data,
15945                 el : opt
15946             });
15947             
15948         }, this);
15949         
15950         this.inputEl().on('change', function(){
15951            this.fireEvent('select', this);
15952         }, this);
15953         
15954     },
15955     
15956     clearIOSView: function()
15957     {
15958         this.inputEl().dom.innerHTML = '';
15959         
15960         this.ios_options = [];
15961     },
15962     
15963     setIOSValue: function(v)
15964     {
15965         this.value = v;
15966         
15967         if(!this.ios_options){
15968             return;
15969         }
15970         
15971         Roo.each(this.ios_options, function(opts){
15972            
15973            opts.el.dom.removeAttribute('selected');
15974            
15975            if(opts.data[this.valueField] != v){
15976                return;
15977            }
15978            
15979            opts.el.dom.setAttribute('selected', true);
15980            
15981         }, this);
15982     }
15983
15984     /** 
15985     * @cfg {Boolean} grow 
15986     * @hide 
15987     */
15988     /** 
15989     * @cfg {Number} growMin 
15990     * @hide 
15991     */
15992     /** 
15993     * @cfg {Number} growMax 
15994     * @hide 
15995     */
15996     /**
15997      * @hide
15998      * @method autoSize
15999      */
16000 });
16001
16002 Roo.apply(Roo.bootstrap.ComboBox,  {
16003     
16004     header : {
16005         tag: 'div',
16006         cls: 'modal-header',
16007         cn: [
16008             {
16009                 tag: 'h4',
16010                 cls: 'modal-title'
16011             }
16012         ]
16013     },
16014     
16015     body : {
16016         tag: 'div',
16017         cls: 'modal-body',
16018         cn: [
16019             {
16020                 tag: 'ul',
16021                 cls: 'list-group'
16022             }
16023         ]
16024     },
16025     
16026     listItemRadio : {
16027         tag: 'li',
16028         cls: 'list-group-item',
16029         cn: [
16030             {
16031                 tag: 'span',
16032                 cls: 'roo-combobox-list-group-item-value'
16033             },
16034             {
16035                 tag: 'div',
16036                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
16037                 cn: [
16038                     {
16039                         tag: 'input',
16040                         type: 'radio'
16041                     },
16042                     {
16043                         tag: 'label'
16044                     }
16045                 ]
16046             }
16047         ]
16048     },
16049     
16050     listItemCheckbox : {
16051         tag: 'li',
16052         cls: 'list-group-item',
16053         cn: [
16054             {
16055                 tag: 'span',
16056                 cls: 'roo-combobox-list-group-item-value'
16057             },
16058             {
16059                 tag: 'div',
16060                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
16061                 cn: [
16062                     {
16063                         tag: 'input',
16064                         type: 'checkbox'
16065                     },
16066                     {
16067                         tag: 'label'
16068                     }
16069                 ]
16070             }
16071         ]
16072     },
16073     
16074     emptyResult : {
16075         tag: 'div',
16076         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
16077     },
16078     
16079     footer : {
16080         tag: 'div',
16081         cls: 'modal-footer',
16082         cn: [
16083             {
16084                 tag: 'div',
16085                 cls: 'row',
16086                 cn: [
16087                     {
16088                         tag: 'div',
16089                         cls: 'col-xs-6 text-left',
16090                         cn: {
16091                             tag: 'button',
16092                             cls: 'btn btn-danger roo-touch-view-cancel',
16093                             html: 'Cancel'
16094                         }
16095                     },
16096                     {
16097                         tag: 'div',
16098                         cls: 'col-xs-6 text-right',
16099                         cn: {
16100                             tag: 'button',
16101                             cls: 'btn btn-success roo-touch-view-ok',
16102                             html: 'OK'
16103                         }
16104                     }
16105                 ]
16106             }
16107         ]
16108         
16109     }
16110 });
16111
16112 Roo.apply(Roo.bootstrap.ComboBox,  {
16113     
16114     touchViewTemplate : {
16115         tag: 'div',
16116         cls: 'modal fade roo-combobox-touch-view',
16117         cn: [
16118             {
16119                 tag: 'div',
16120                 cls: 'modal-dialog',
16121                 style : 'position:fixed', // we have to fix position....
16122                 cn: [
16123                     {
16124                         tag: 'div',
16125                         cls: 'modal-content',
16126                         cn: [
16127                             Roo.bootstrap.ComboBox.header,
16128                             Roo.bootstrap.ComboBox.body,
16129                             Roo.bootstrap.ComboBox.footer
16130                         ]
16131                     }
16132                 ]
16133             }
16134         ]
16135     }
16136 });/*
16137  * Based on:
16138  * Ext JS Library 1.1.1
16139  * Copyright(c) 2006-2007, Ext JS, LLC.
16140  *
16141  * Originally Released Under LGPL - original licence link has changed is not relivant.
16142  *
16143  * Fork - LGPL
16144  * <script type="text/javascript">
16145  */
16146
16147 /**
16148  * @class Roo.View
16149  * @extends Roo.util.Observable
16150  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
16151  * This class also supports single and multi selection modes. <br>
16152  * Create a data model bound view:
16153  <pre><code>
16154  var store = new Roo.data.Store(...);
16155
16156  var view = new Roo.View({
16157     el : "my-element",
16158     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
16159  
16160     singleSelect: true,
16161     selectedClass: "ydataview-selected",
16162     store: store
16163  });
16164
16165  // listen for node click?
16166  view.on("click", function(vw, index, node, e){
16167  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
16168  });
16169
16170  // load XML data
16171  dataModel.load("foobar.xml");
16172  </code></pre>
16173  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
16174  * <br><br>
16175  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
16176  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
16177  * 
16178  * Note: old style constructor is still suported (container, template, config)
16179  * 
16180  * @constructor
16181  * Create a new View
16182  * @param {Object} config The config object
16183  * 
16184  */
16185 Roo.View = function(config, depreciated_tpl, depreciated_config){
16186     
16187     this.parent = false;
16188     
16189     if (typeof(depreciated_tpl) == 'undefined') {
16190         // new way.. - universal constructor.
16191         Roo.apply(this, config);
16192         this.el  = Roo.get(this.el);
16193     } else {
16194         // old format..
16195         this.el  = Roo.get(config);
16196         this.tpl = depreciated_tpl;
16197         Roo.apply(this, depreciated_config);
16198     }
16199     this.wrapEl  = this.el.wrap().wrap();
16200     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
16201     
16202     
16203     if(typeof(this.tpl) == "string"){
16204         this.tpl = new Roo.Template(this.tpl);
16205     } else {
16206         // support xtype ctors..
16207         this.tpl = new Roo.factory(this.tpl, Roo);
16208     }
16209     
16210     
16211     this.tpl.compile();
16212     
16213     /** @private */
16214     this.addEvents({
16215         /**
16216          * @event beforeclick
16217          * Fires before a click is processed. Returns false to cancel the default action.
16218          * @param {Roo.View} this
16219          * @param {Number} index The index of the target node
16220          * @param {HTMLElement} node The target node
16221          * @param {Roo.EventObject} e The raw event object
16222          */
16223             "beforeclick" : true,
16224         /**
16225          * @event click
16226          * Fires when a template node is clicked.
16227          * @param {Roo.View} this
16228          * @param {Number} index The index of the target node
16229          * @param {HTMLElement} node The target node
16230          * @param {Roo.EventObject} e The raw event object
16231          */
16232             "click" : true,
16233         /**
16234          * @event dblclick
16235          * Fires when a template node is double clicked.
16236          * @param {Roo.View} this
16237          * @param {Number} index The index of the target node
16238          * @param {HTMLElement} node The target node
16239          * @param {Roo.EventObject} e The raw event object
16240          */
16241             "dblclick" : true,
16242         /**
16243          * @event contextmenu
16244          * Fires when a template node is right clicked.
16245          * @param {Roo.View} this
16246          * @param {Number} index The index of the target node
16247          * @param {HTMLElement} node The target node
16248          * @param {Roo.EventObject} e The raw event object
16249          */
16250             "contextmenu" : true,
16251         /**
16252          * @event selectionchange
16253          * Fires when the selected nodes change.
16254          * @param {Roo.View} this
16255          * @param {Array} selections Array of the selected nodes
16256          */
16257             "selectionchange" : true,
16258     
16259         /**
16260          * @event beforeselect
16261          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
16262          * @param {Roo.View} this
16263          * @param {HTMLElement} node The node to be selected
16264          * @param {Array} selections Array of currently selected nodes
16265          */
16266             "beforeselect" : true,
16267         /**
16268          * @event preparedata
16269          * Fires on every row to render, to allow you to change the data.
16270          * @param {Roo.View} this
16271          * @param {Object} data to be rendered (change this)
16272          */
16273           "preparedata" : true
16274           
16275           
16276         });
16277
16278
16279
16280     this.el.on({
16281         "click": this.onClick,
16282         "dblclick": this.onDblClick,
16283         "contextmenu": this.onContextMenu,
16284         scope:this
16285     });
16286
16287     this.selections = [];
16288     this.nodes = [];
16289     this.cmp = new Roo.CompositeElementLite([]);
16290     if(this.store){
16291         this.store = Roo.factory(this.store, Roo.data);
16292         this.setStore(this.store, true);
16293     }
16294     
16295     if ( this.footer && this.footer.xtype) {
16296            
16297          var fctr = this.wrapEl.appendChild(document.createElement("div"));
16298         
16299         this.footer.dataSource = this.store;
16300         this.footer.container = fctr;
16301         this.footer = Roo.factory(this.footer, Roo);
16302         fctr.insertFirst(this.el);
16303         
16304         // this is a bit insane - as the paging toolbar seems to detach the el..
16305 //        dom.parentNode.parentNode.parentNode
16306          // they get detached?
16307     }
16308     
16309     
16310     Roo.View.superclass.constructor.call(this);
16311     
16312     
16313 };
16314
16315 Roo.extend(Roo.View, Roo.util.Observable, {
16316     
16317      /**
16318      * @cfg {Roo.data.Store} store Data store to load data from.
16319      */
16320     store : false,
16321     
16322     /**
16323      * @cfg {String|Roo.Element} el The container element.
16324      */
16325     el : '',
16326     
16327     /**
16328      * @cfg {String|Roo.Template} tpl The template used by this View 
16329      */
16330     tpl : false,
16331     /**
16332      * @cfg {String} dataName the named area of the template to use as the data area
16333      *                          Works with domtemplates roo-name="name"
16334      */
16335     dataName: false,
16336     /**
16337      * @cfg {String} selectedClass The css class to add to selected nodes
16338      */
16339     selectedClass : "x-view-selected",
16340      /**
16341      * @cfg {String} emptyText The empty text to show when nothing is loaded.
16342      */
16343     emptyText : "",
16344     
16345     /**
16346      * @cfg {String} text to display on mask (default Loading)
16347      */
16348     mask : false,
16349     /**
16350      * @cfg {Boolean} multiSelect Allow multiple selection
16351      */
16352     multiSelect : false,
16353     /**
16354      * @cfg {Boolean} singleSelect Allow single selection
16355      */
16356     singleSelect:  false,
16357     
16358     /**
16359      * @cfg {Boolean} toggleSelect - selecting 
16360      */
16361     toggleSelect : false,
16362     
16363     /**
16364      * @cfg {Boolean} tickable - selecting 
16365      */
16366     tickable : false,
16367     
16368     /**
16369      * Returns the element this view is bound to.
16370      * @return {Roo.Element}
16371      */
16372     getEl : function(){
16373         return this.wrapEl;
16374     },
16375     
16376     
16377
16378     /**
16379      * Refreshes the view. - called by datachanged on the store. - do not call directly.
16380      */
16381     refresh : function(){
16382         //Roo.log('refresh');
16383         var t = this.tpl;
16384         
16385         // if we are using something like 'domtemplate', then
16386         // the what gets used is:
16387         // t.applySubtemplate(NAME, data, wrapping data..)
16388         // the outer template then get' applied with
16389         //     the store 'extra data'
16390         // and the body get's added to the
16391         //      roo-name="data" node?
16392         //      <span class='roo-tpl-{name}'></span> ?????
16393         
16394         
16395         
16396         this.clearSelections();
16397         this.el.update("");
16398         var html = [];
16399         var records = this.store.getRange();
16400         if(records.length < 1) {
16401             
16402             // is this valid??  = should it render a template??
16403             
16404             this.el.update(this.emptyText);
16405             return;
16406         }
16407         var el = this.el;
16408         if (this.dataName) {
16409             this.el.update(t.apply(this.store.meta)); //????
16410             el = this.el.child('.roo-tpl-' + this.dataName);
16411         }
16412         
16413         for(var i = 0, len = records.length; i < len; i++){
16414             var data = this.prepareData(records[i].data, i, records[i]);
16415             this.fireEvent("preparedata", this, data, i, records[i]);
16416             
16417             var d = Roo.apply({}, data);
16418             
16419             if(this.tickable){
16420                 Roo.apply(d, {'roo-id' : Roo.id()});
16421                 
16422                 var _this = this;
16423             
16424                 Roo.each(this.parent.item, function(item){
16425                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
16426                         return;
16427                     }
16428                     Roo.apply(d, {'roo-data-checked' : 'checked'});
16429                 });
16430             }
16431             
16432             html[html.length] = Roo.util.Format.trim(
16433                 this.dataName ?
16434                     t.applySubtemplate(this.dataName, d, this.store.meta) :
16435                     t.apply(d)
16436             );
16437         }
16438         
16439         
16440         
16441         el.update(html.join(""));
16442         this.nodes = el.dom.childNodes;
16443         this.updateIndexes(0);
16444     },
16445     
16446
16447     /**
16448      * Function to override to reformat the data that is sent to
16449      * the template for each node.
16450      * DEPRICATED - use the preparedata event handler.
16451      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
16452      * a JSON object for an UpdateManager bound view).
16453      */
16454     prepareData : function(data, index, record)
16455     {
16456         this.fireEvent("preparedata", this, data, index, record);
16457         return data;
16458     },
16459
16460     onUpdate : function(ds, record){
16461         // Roo.log('on update');   
16462         this.clearSelections();
16463         var index = this.store.indexOf(record);
16464         var n = this.nodes[index];
16465         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
16466         n.parentNode.removeChild(n);
16467         this.updateIndexes(index, index);
16468     },
16469
16470     
16471     
16472 // --------- FIXME     
16473     onAdd : function(ds, records, index)
16474     {
16475         //Roo.log(['on Add', ds, records, index] );        
16476         this.clearSelections();
16477         if(this.nodes.length == 0){
16478             this.refresh();
16479             return;
16480         }
16481         var n = this.nodes[index];
16482         for(var i = 0, len = records.length; i < len; i++){
16483             var d = this.prepareData(records[i].data, i, records[i]);
16484             if(n){
16485                 this.tpl.insertBefore(n, d);
16486             }else{
16487                 
16488                 this.tpl.append(this.el, d);
16489             }
16490         }
16491         this.updateIndexes(index);
16492     },
16493
16494     onRemove : function(ds, record, index){
16495        // Roo.log('onRemove');
16496         this.clearSelections();
16497         var el = this.dataName  ?
16498             this.el.child('.roo-tpl-' + this.dataName) :
16499             this.el; 
16500         
16501         el.dom.removeChild(this.nodes[index]);
16502         this.updateIndexes(index);
16503     },
16504
16505     /**
16506      * Refresh an individual node.
16507      * @param {Number} index
16508      */
16509     refreshNode : function(index){
16510         this.onUpdate(this.store, this.store.getAt(index));
16511     },
16512
16513     updateIndexes : function(startIndex, endIndex){
16514         var ns = this.nodes;
16515         startIndex = startIndex || 0;
16516         endIndex = endIndex || ns.length - 1;
16517         for(var i = startIndex; i <= endIndex; i++){
16518             ns[i].nodeIndex = i;
16519         }
16520     },
16521
16522     /**
16523      * Changes the data store this view uses and refresh the view.
16524      * @param {Store} store
16525      */
16526     setStore : function(store, initial){
16527         if(!initial && this.store){
16528             this.store.un("datachanged", this.refresh);
16529             this.store.un("add", this.onAdd);
16530             this.store.un("remove", this.onRemove);
16531             this.store.un("update", this.onUpdate);
16532             this.store.un("clear", this.refresh);
16533             this.store.un("beforeload", this.onBeforeLoad);
16534             this.store.un("load", this.onLoad);
16535             this.store.un("loadexception", this.onLoad);
16536         }
16537         if(store){
16538           
16539             store.on("datachanged", this.refresh, this);
16540             store.on("add", this.onAdd, this);
16541             store.on("remove", this.onRemove, this);
16542             store.on("update", this.onUpdate, this);
16543             store.on("clear", this.refresh, this);
16544             store.on("beforeload", this.onBeforeLoad, this);
16545             store.on("load", this.onLoad, this);
16546             store.on("loadexception", this.onLoad, this);
16547         }
16548         
16549         if(store){
16550             this.refresh();
16551         }
16552     },
16553     /**
16554      * onbeforeLoad - masks the loading area.
16555      *
16556      */
16557     onBeforeLoad : function(store,opts)
16558     {
16559          //Roo.log('onBeforeLoad');   
16560         if (!opts.add) {
16561             this.el.update("");
16562         }
16563         this.el.mask(this.mask ? this.mask : "Loading" ); 
16564     },
16565     onLoad : function ()
16566     {
16567         this.el.unmask();
16568     },
16569     
16570
16571     /**
16572      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
16573      * @param {HTMLElement} node
16574      * @return {HTMLElement} The template node
16575      */
16576     findItemFromChild : function(node){
16577         var el = this.dataName  ?
16578             this.el.child('.roo-tpl-' + this.dataName,true) :
16579             this.el.dom; 
16580         
16581         if(!node || node.parentNode == el){
16582                     return node;
16583             }
16584             var p = node.parentNode;
16585             while(p && p != el){
16586             if(p.parentNode == el){
16587                 return p;
16588             }
16589             p = p.parentNode;
16590         }
16591             return null;
16592     },
16593
16594     /** @ignore */
16595     onClick : function(e){
16596         var item = this.findItemFromChild(e.getTarget());
16597         if(item){
16598             var index = this.indexOf(item);
16599             if(this.onItemClick(item, index, e) !== false){
16600                 this.fireEvent("click", this, index, item, e);
16601             }
16602         }else{
16603             this.clearSelections();
16604         }
16605     },
16606
16607     /** @ignore */
16608     onContextMenu : function(e){
16609         var item = this.findItemFromChild(e.getTarget());
16610         if(item){
16611             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16612         }
16613     },
16614
16615     /** @ignore */
16616     onDblClick : function(e){
16617         var item = this.findItemFromChild(e.getTarget());
16618         if(item){
16619             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16620         }
16621     },
16622
16623     onItemClick : function(item, index, e)
16624     {
16625         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16626             return false;
16627         }
16628         if (this.toggleSelect) {
16629             var m = this.isSelected(item) ? 'unselect' : 'select';
16630             //Roo.log(m);
16631             var _t = this;
16632             _t[m](item, true, false);
16633             return true;
16634         }
16635         if(this.multiSelect || this.singleSelect){
16636             if(this.multiSelect && e.shiftKey && this.lastSelection){
16637                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16638             }else{
16639                 this.select(item, this.multiSelect && e.ctrlKey);
16640                 this.lastSelection = item;
16641             }
16642             
16643             if(!this.tickable){
16644                 e.preventDefault();
16645             }
16646             
16647         }
16648         return true;
16649     },
16650
16651     /**
16652      * Get the number of selected nodes.
16653      * @return {Number}
16654      */
16655     getSelectionCount : function(){
16656         return this.selections.length;
16657     },
16658
16659     /**
16660      * Get the currently selected nodes.
16661      * @return {Array} An array of HTMLElements
16662      */
16663     getSelectedNodes : function(){
16664         return this.selections;
16665     },
16666
16667     /**
16668      * Get the indexes of the selected nodes.
16669      * @return {Array}
16670      */
16671     getSelectedIndexes : function(){
16672         var indexes = [], s = this.selections;
16673         for(var i = 0, len = s.length; i < len; i++){
16674             indexes.push(s[i].nodeIndex);
16675         }
16676         return indexes;
16677     },
16678
16679     /**
16680      * Clear all selections
16681      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16682      */
16683     clearSelections : function(suppressEvent){
16684         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16685             this.cmp.elements = this.selections;
16686             this.cmp.removeClass(this.selectedClass);
16687             this.selections = [];
16688             if(!suppressEvent){
16689                 this.fireEvent("selectionchange", this, this.selections);
16690             }
16691         }
16692     },
16693
16694     /**
16695      * Returns true if the passed node is selected
16696      * @param {HTMLElement/Number} node The node or node index
16697      * @return {Boolean}
16698      */
16699     isSelected : function(node){
16700         var s = this.selections;
16701         if(s.length < 1){
16702             return false;
16703         }
16704         node = this.getNode(node);
16705         return s.indexOf(node) !== -1;
16706     },
16707
16708     /**
16709      * Selects nodes.
16710      * @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
16711      * @param {Boolean} keepExisting (optional) true to keep existing selections
16712      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16713      */
16714     select : function(nodeInfo, keepExisting, suppressEvent){
16715         if(nodeInfo instanceof Array){
16716             if(!keepExisting){
16717                 this.clearSelections(true);
16718             }
16719             for(var i = 0, len = nodeInfo.length; i < len; i++){
16720                 this.select(nodeInfo[i], true, true);
16721             }
16722             return;
16723         } 
16724         var node = this.getNode(nodeInfo);
16725         if(!node || this.isSelected(node)){
16726             return; // already selected.
16727         }
16728         if(!keepExisting){
16729             this.clearSelections(true);
16730         }
16731         
16732         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16733             Roo.fly(node).addClass(this.selectedClass);
16734             this.selections.push(node);
16735             if(!suppressEvent){
16736                 this.fireEvent("selectionchange", this, this.selections);
16737             }
16738         }
16739         
16740         
16741     },
16742       /**
16743      * Unselects nodes.
16744      * @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
16745      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16746      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16747      */
16748     unselect : function(nodeInfo, keepExisting, suppressEvent)
16749     {
16750         if(nodeInfo instanceof Array){
16751             Roo.each(this.selections, function(s) {
16752                 this.unselect(s, nodeInfo);
16753             }, this);
16754             return;
16755         }
16756         var node = this.getNode(nodeInfo);
16757         if(!node || !this.isSelected(node)){
16758             //Roo.log("not selected");
16759             return; // not selected.
16760         }
16761         // fireevent???
16762         var ns = [];
16763         Roo.each(this.selections, function(s) {
16764             if (s == node ) {
16765                 Roo.fly(node).removeClass(this.selectedClass);
16766
16767                 return;
16768             }
16769             ns.push(s);
16770         },this);
16771         
16772         this.selections= ns;
16773         this.fireEvent("selectionchange", this, this.selections);
16774     },
16775
16776     /**
16777      * Gets a template node.
16778      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16779      * @return {HTMLElement} The node or null if it wasn't found
16780      */
16781     getNode : function(nodeInfo){
16782         if(typeof nodeInfo == "string"){
16783             return document.getElementById(nodeInfo);
16784         }else if(typeof nodeInfo == "number"){
16785             return this.nodes[nodeInfo];
16786         }
16787         return nodeInfo;
16788     },
16789
16790     /**
16791      * Gets a range template nodes.
16792      * @param {Number} startIndex
16793      * @param {Number} endIndex
16794      * @return {Array} An array of nodes
16795      */
16796     getNodes : function(start, end){
16797         var ns = this.nodes;
16798         start = start || 0;
16799         end = typeof end == "undefined" ? ns.length - 1 : end;
16800         var nodes = [];
16801         if(start <= end){
16802             for(var i = start; i <= end; i++){
16803                 nodes.push(ns[i]);
16804             }
16805         } else{
16806             for(var i = start; i >= end; i--){
16807                 nodes.push(ns[i]);
16808             }
16809         }
16810         return nodes;
16811     },
16812
16813     /**
16814      * Finds the index of the passed node
16815      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16816      * @return {Number} The index of the node or -1
16817      */
16818     indexOf : function(node){
16819         node = this.getNode(node);
16820         if(typeof node.nodeIndex == "number"){
16821             return node.nodeIndex;
16822         }
16823         var ns = this.nodes;
16824         for(var i = 0, len = ns.length; i < len; i++){
16825             if(ns[i] == node){
16826                 return i;
16827             }
16828         }
16829         return -1;
16830     }
16831 });
16832 /*
16833  * - LGPL
16834  *
16835  * based on jquery fullcalendar
16836  * 
16837  */
16838
16839 Roo.bootstrap = Roo.bootstrap || {};
16840 /**
16841  * @class Roo.bootstrap.Calendar
16842  * @extends Roo.bootstrap.Component
16843  * Bootstrap Calendar class
16844  * @cfg {Boolean} loadMask (true|false) default false
16845  * @cfg {Object} header generate the user specific header of the calendar, default false
16846
16847  * @constructor
16848  * Create a new Container
16849  * @param {Object} config The config object
16850  */
16851
16852
16853
16854 Roo.bootstrap.Calendar = function(config){
16855     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16856      this.addEvents({
16857         /**
16858              * @event select
16859              * Fires when a date is selected
16860              * @param {DatePicker} this
16861              * @param {Date} date The selected date
16862              */
16863         'select': true,
16864         /**
16865              * @event monthchange
16866              * Fires when the displayed month changes 
16867              * @param {DatePicker} this
16868              * @param {Date} date The selected month
16869              */
16870         'monthchange': true,
16871         /**
16872              * @event evententer
16873              * Fires when mouse over an event
16874              * @param {Calendar} this
16875              * @param {event} Event
16876              */
16877         'evententer': true,
16878         /**
16879              * @event eventleave
16880              * Fires when the mouse leaves an
16881              * @param {Calendar} this
16882              * @param {event}
16883              */
16884         'eventleave': true,
16885         /**
16886              * @event eventclick
16887              * Fires when the mouse click an
16888              * @param {Calendar} this
16889              * @param {event}
16890              */
16891         'eventclick': true
16892         
16893     });
16894
16895 };
16896
16897 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16898     
16899      /**
16900      * @cfg {Number} startDay
16901      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16902      */
16903     startDay : 0,
16904     
16905     loadMask : false,
16906     
16907     header : false,
16908       
16909     getAutoCreate : function(){
16910         
16911         
16912         var fc_button = function(name, corner, style, content ) {
16913             return Roo.apply({},{
16914                 tag : 'span',
16915                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16916                          (corner.length ?
16917                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16918                             ''
16919                         ),
16920                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16921                 unselectable: 'on'
16922             });
16923         };
16924         
16925         var header = {};
16926         
16927         if(!this.header){
16928             header = {
16929                 tag : 'table',
16930                 cls : 'fc-header',
16931                 style : 'width:100%',
16932                 cn : [
16933                     {
16934                         tag: 'tr',
16935                         cn : [
16936                             {
16937                                 tag : 'td',
16938                                 cls : 'fc-header-left',
16939                                 cn : [
16940                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16941                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16942                                     { tag: 'span', cls: 'fc-header-space' },
16943                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16944
16945
16946                                 ]
16947                             },
16948
16949                             {
16950                                 tag : 'td',
16951                                 cls : 'fc-header-center',
16952                                 cn : [
16953                                     {
16954                                         tag: 'span',
16955                                         cls: 'fc-header-title',
16956                                         cn : {
16957                                             tag: 'H2',
16958                                             html : 'month / year'
16959                                         }
16960                                     }
16961
16962                                 ]
16963                             },
16964                             {
16965                                 tag : 'td',
16966                                 cls : 'fc-header-right',
16967                                 cn : [
16968                               /*      fc_button('month', 'left', '', 'month' ),
16969                                     fc_button('week', '', '', 'week' ),
16970                                     fc_button('day', 'right', '', 'day' )
16971                                 */    
16972
16973                                 ]
16974                             }
16975
16976                         ]
16977                     }
16978                 ]
16979             };
16980         }
16981         
16982         header = this.header;
16983         
16984        
16985         var cal_heads = function() {
16986             var ret = [];
16987             // fixme - handle this.
16988             
16989             for (var i =0; i < Date.dayNames.length; i++) {
16990                 var d = Date.dayNames[i];
16991                 ret.push({
16992                     tag: 'th',
16993                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16994                     html : d.substring(0,3)
16995                 });
16996                 
16997             }
16998             ret[0].cls += ' fc-first';
16999             ret[6].cls += ' fc-last';
17000             return ret;
17001         };
17002         var cal_cell = function(n) {
17003             return  {
17004                 tag: 'td',
17005                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
17006                 cn : [
17007                     {
17008                         cn : [
17009                             {
17010                                 cls: 'fc-day-number',
17011                                 html: 'D'
17012                             },
17013                             {
17014                                 cls: 'fc-day-content',
17015                              
17016                                 cn : [
17017                                      {
17018                                         style: 'position: relative;' // height: 17px;
17019                                     }
17020                                 ]
17021                             }
17022                             
17023                             
17024                         ]
17025                     }
17026                 ]
17027                 
17028             }
17029         };
17030         var cal_rows = function() {
17031             
17032             var ret = [];
17033             for (var r = 0; r < 6; r++) {
17034                 var row= {
17035                     tag : 'tr',
17036                     cls : 'fc-week',
17037                     cn : []
17038                 };
17039                 
17040                 for (var i =0; i < Date.dayNames.length; i++) {
17041                     var d = Date.dayNames[i];
17042                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
17043
17044                 }
17045                 row.cn[0].cls+=' fc-first';
17046                 row.cn[0].cn[0].style = 'min-height:90px';
17047                 row.cn[6].cls+=' fc-last';
17048                 ret.push(row);
17049                 
17050             }
17051             ret[0].cls += ' fc-first';
17052             ret[4].cls += ' fc-prev-last';
17053             ret[5].cls += ' fc-last';
17054             return ret;
17055             
17056         };
17057         
17058         var cal_table = {
17059             tag: 'table',
17060             cls: 'fc-border-separate',
17061             style : 'width:100%',
17062             cellspacing  : 0,
17063             cn : [
17064                 { 
17065                     tag: 'thead',
17066                     cn : [
17067                         { 
17068                             tag: 'tr',
17069                             cls : 'fc-first fc-last',
17070                             cn : cal_heads()
17071                         }
17072                     ]
17073                 },
17074                 { 
17075                     tag: 'tbody',
17076                     cn : cal_rows()
17077                 }
17078                   
17079             ]
17080         };
17081          
17082          var cfg = {
17083             cls : 'fc fc-ltr',
17084             cn : [
17085                 header,
17086                 {
17087                     cls : 'fc-content',
17088                     style : "position: relative;",
17089                     cn : [
17090                         {
17091                             cls : 'fc-view fc-view-month fc-grid',
17092                             style : 'position: relative',
17093                             unselectable : 'on',
17094                             cn : [
17095                                 {
17096                                     cls : 'fc-event-container',
17097                                     style : 'position:absolute;z-index:8;top:0;left:0;'
17098                                 },
17099                                 cal_table
17100                             ]
17101                         }
17102                     ]
17103     
17104                 }
17105            ] 
17106             
17107         };
17108         
17109          
17110         
17111         return cfg;
17112     },
17113     
17114     
17115     initEvents : function()
17116     {
17117         if(!this.store){
17118             throw "can not find store for calendar";
17119         }
17120         
17121         var mark = {
17122             tag: "div",
17123             cls:"x-dlg-mask",
17124             style: "text-align:center",
17125             cn: [
17126                 {
17127                     tag: "div",
17128                     style: "background-color:white;width:50%;margin:250 auto",
17129                     cn: [
17130                         {
17131                             tag: "img",
17132                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
17133                         },
17134                         {
17135                             tag: "span",
17136                             html: "Loading"
17137                         }
17138                         
17139                     ]
17140                 }
17141             ]
17142         };
17143         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
17144         
17145         var size = this.el.select('.fc-content', true).first().getSize();
17146         this.maskEl.setSize(size.width, size.height);
17147         this.maskEl.enableDisplayMode("block");
17148         if(!this.loadMask){
17149             this.maskEl.hide();
17150         }
17151         
17152         this.store = Roo.factory(this.store, Roo.data);
17153         this.store.on('load', this.onLoad, this);
17154         this.store.on('beforeload', this.onBeforeLoad, this);
17155         
17156         this.resize();
17157         
17158         this.cells = this.el.select('.fc-day',true);
17159         //Roo.log(this.cells);
17160         this.textNodes = this.el.query('.fc-day-number');
17161         this.cells.addClassOnOver('fc-state-hover');
17162         
17163         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
17164         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
17165         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
17166         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
17167         
17168         this.on('monthchange', this.onMonthChange, this);
17169         
17170         this.update(new Date().clearTime());
17171     },
17172     
17173     resize : function() {
17174         var sz  = this.el.getSize();
17175         
17176         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
17177         this.el.select('.fc-day-content div',true).setHeight(34);
17178     },
17179     
17180     
17181     // private
17182     showPrevMonth : function(e){
17183         this.update(this.activeDate.add("mo", -1));
17184     },
17185     showToday : function(e){
17186         this.update(new Date().clearTime());
17187     },
17188     // private
17189     showNextMonth : function(e){
17190         this.update(this.activeDate.add("mo", 1));
17191     },
17192
17193     // private
17194     showPrevYear : function(){
17195         this.update(this.activeDate.add("y", -1));
17196     },
17197
17198     // private
17199     showNextYear : function(){
17200         this.update(this.activeDate.add("y", 1));
17201     },
17202
17203     
17204    // private
17205     update : function(date)
17206     {
17207         var vd = this.activeDate;
17208         this.activeDate = date;
17209 //        if(vd && this.el){
17210 //            var t = date.getTime();
17211 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
17212 //                Roo.log('using add remove');
17213 //                
17214 //                this.fireEvent('monthchange', this, date);
17215 //                
17216 //                this.cells.removeClass("fc-state-highlight");
17217 //                this.cells.each(function(c){
17218 //                   if(c.dateValue == t){
17219 //                       c.addClass("fc-state-highlight");
17220 //                       setTimeout(function(){
17221 //                            try{c.dom.firstChild.focus();}catch(e){}
17222 //                       }, 50);
17223 //                       return false;
17224 //                   }
17225 //                   return true;
17226 //                });
17227 //                return;
17228 //            }
17229 //        }
17230         
17231         var days = date.getDaysInMonth();
17232         
17233         var firstOfMonth = date.getFirstDateOfMonth();
17234         var startingPos = firstOfMonth.getDay()-this.startDay;
17235         
17236         if(startingPos < this.startDay){
17237             startingPos += 7;
17238         }
17239         
17240         var pm = date.add(Date.MONTH, -1);
17241         var prevStart = pm.getDaysInMonth()-startingPos;
17242 //        
17243         this.cells = this.el.select('.fc-day',true);
17244         this.textNodes = this.el.query('.fc-day-number');
17245         this.cells.addClassOnOver('fc-state-hover');
17246         
17247         var cells = this.cells.elements;
17248         var textEls = this.textNodes;
17249         
17250         Roo.each(cells, function(cell){
17251             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
17252         });
17253         
17254         days += startingPos;
17255
17256         // convert everything to numbers so it's fast
17257         var day = 86400000;
17258         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
17259         //Roo.log(d);
17260         //Roo.log(pm);
17261         //Roo.log(prevStart);
17262         
17263         var today = new Date().clearTime().getTime();
17264         var sel = date.clearTime().getTime();
17265         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
17266         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
17267         var ddMatch = this.disabledDatesRE;
17268         var ddText = this.disabledDatesText;
17269         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
17270         var ddaysText = this.disabledDaysText;
17271         var format = this.format;
17272         
17273         var setCellClass = function(cal, cell){
17274             cell.row = 0;
17275             cell.events = [];
17276             cell.more = [];
17277             //Roo.log('set Cell Class');
17278             cell.title = "";
17279             var t = d.getTime();
17280             
17281             //Roo.log(d);
17282             
17283             cell.dateValue = t;
17284             if(t == today){
17285                 cell.className += " fc-today";
17286                 cell.className += " fc-state-highlight";
17287                 cell.title = cal.todayText;
17288             }
17289             if(t == sel){
17290                 // disable highlight in other month..
17291                 //cell.className += " fc-state-highlight";
17292                 
17293             }
17294             // disabling
17295             if(t < min) {
17296                 cell.className = " fc-state-disabled";
17297                 cell.title = cal.minText;
17298                 return;
17299             }
17300             if(t > max) {
17301                 cell.className = " fc-state-disabled";
17302                 cell.title = cal.maxText;
17303                 return;
17304             }
17305             if(ddays){
17306                 if(ddays.indexOf(d.getDay()) != -1){
17307                     cell.title = ddaysText;
17308                     cell.className = " fc-state-disabled";
17309                 }
17310             }
17311             if(ddMatch && format){
17312                 var fvalue = d.dateFormat(format);
17313                 if(ddMatch.test(fvalue)){
17314                     cell.title = ddText.replace("%0", fvalue);
17315                     cell.className = " fc-state-disabled";
17316                 }
17317             }
17318             
17319             if (!cell.initialClassName) {
17320                 cell.initialClassName = cell.dom.className;
17321             }
17322             
17323             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
17324         };
17325
17326         var i = 0;
17327         
17328         for(; i < startingPos; i++) {
17329             textEls[i].innerHTML = (++prevStart);
17330             d.setDate(d.getDate()+1);
17331             
17332             cells[i].className = "fc-past fc-other-month";
17333             setCellClass(this, cells[i]);
17334         }
17335         
17336         var intDay = 0;
17337         
17338         for(; i < days; i++){
17339             intDay = i - startingPos + 1;
17340             textEls[i].innerHTML = (intDay);
17341             d.setDate(d.getDate()+1);
17342             
17343             cells[i].className = ''; // "x-date-active";
17344             setCellClass(this, cells[i]);
17345         }
17346         var extraDays = 0;
17347         
17348         for(; i < 42; i++) {
17349             textEls[i].innerHTML = (++extraDays);
17350             d.setDate(d.getDate()+1);
17351             
17352             cells[i].className = "fc-future fc-other-month";
17353             setCellClass(this, cells[i]);
17354         }
17355         
17356         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
17357         
17358         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
17359         
17360         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
17361         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
17362         
17363         if(totalRows != 6){
17364             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
17365             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
17366         }
17367         
17368         this.fireEvent('monthchange', this, date);
17369         
17370         
17371         /*
17372         if(!this.internalRender){
17373             var main = this.el.dom.firstChild;
17374             var w = main.offsetWidth;
17375             this.el.setWidth(w + this.el.getBorderWidth("lr"));
17376             Roo.fly(main).setWidth(w);
17377             this.internalRender = true;
17378             // opera does not respect the auto grow header center column
17379             // then, after it gets a width opera refuses to recalculate
17380             // without a second pass
17381             if(Roo.isOpera && !this.secondPass){
17382                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
17383                 this.secondPass = true;
17384                 this.update.defer(10, this, [date]);
17385             }
17386         }
17387         */
17388         
17389     },
17390     
17391     findCell : function(dt) {
17392         dt = dt.clearTime().getTime();
17393         var ret = false;
17394         this.cells.each(function(c){
17395             //Roo.log("check " +c.dateValue + '?=' + dt);
17396             if(c.dateValue == dt){
17397                 ret = c;
17398                 return false;
17399             }
17400             return true;
17401         });
17402         
17403         return ret;
17404     },
17405     
17406     findCells : function(ev) {
17407         var s = ev.start.clone().clearTime().getTime();
17408        // Roo.log(s);
17409         var e= ev.end.clone().clearTime().getTime();
17410        // Roo.log(e);
17411         var ret = [];
17412         this.cells.each(function(c){
17413              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
17414             
17415             if(c.dateValue > e){
17416                 return ;
17417             }
17418             if(c.dateValue < s){
17419                 return ;
17420             }
17421             ret.push(c);
17422         });
17423         
17424         return ret;    
17425     },
17426     
17427 //    findBestRow: function(cells)
17428 //    {
17429 //        var ret = 0;
17430 //        
17431 //        for (var i =0 ; i < cells.length;i++) {
17432 //            ret  = Math.max(cells[i].rows || 0,ret);
17433 //        }
17434 //        return ret;
17435 //        
17436 //    },
17437     
17438     
17439     addItem : function(ev)
17440     {
17441         // look for vertical location slot in
17442         var cells = this.findCells(ev);
17443         
17444 //        ev.row = this.findBestRow(cells);
17445         
17446         // work out the location.
17447         
17448         var crow = false;
17449         var rows = [];
17450         for(var i =0; i < cells.length; i++) {
17451             
17452             cells[i].row = cells[0].row;
17453             
17454             if(i == 0){
17455                 cells[i].row = cells[i].row + 1;
17456             }
17457             
17458             if (!crow) {
17459                 crow = {
17460                     start : cells[i],
17461                     end :  cells[i]
17462                 };
17463                 continue;
17464             }
17465             if (crow.start.getY() == cells[i].getY()) {
17466                 // on same row.
17467                 crow.end = cells[i];
17468                 continue;
17469             }
17470             // different row.
17471             rows.push(crow);
17472             crow = {
17473                 start: cells[i],
17474                 end : cells[i]
17475             };
17476             
17477         }
17478         
17479         rows.push(crow);
17480         ev.els = [];
17481         ev.rows = rows;
17482         ev.cells = cells;
17483         
17484         cells[0].events.push(ev);
17485         
17486         this.calevents.push(ev);
17487     },
17488     
17489     clearEvents: function() {
17490         
17491         if(!this.calevents){
17492             return;
17493         }
17494         
17495         Roo.each(this.cells.elements, function(c){
17496             c.row = 0;
17497             c.events = [];
17498             c.more = [];
17499         });
17500         
17501         Roo.each(this.calevents, function(e) {
17502             Roo.each(e.els, function(el) {
17503                 el.un('mouseenter' ,this.onEventEnter, this);
17504                 el.un('mouseleave' ,this.onEventLeave, this);
17505                 el.remove();
17506             },this);
17507         },this);
17508         
17509         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
17510             e.remove();
17511         });
17512         
17513     },
17514     
17515     renderEvents: function()
17516     {   
17517         var _this = this;
17518         
17519         this.cells.each(function(c) {
17520             
17521             if(c.row < 5){
17522                 return;
17523             }
17524             
17525             var ev = c.events;
17526             
17527             var r = 4;
17528             if(c.row != c.events.length){
17529                 r = 4 - (4 - (c.row - c.events.length));
17530             }
17531             
17532             c.events = ev.slice(0, r);
17533             c.more = ev.slice(r);
17534             
17535             if(c.more.length && c.more.length == 1){
17536                 c.events.push(c.more.pop());
17537             }
17538             
17539             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
17540             
17541         });
17542             
17543         this.cells.each(function(c) {
17544             
17545             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
17546             
17547             
17548             for (var e = 0; e < c.events.length; e++){
17549                 var ev = c.events[e];
17550                 var rows = ev.rows;
17551                 
17552                 for(var i = 0; i < rows.length; i++) {
17553                 
17554                     // how many rows should it span..
17555
17556                     var  cfg = {
17557                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
17558                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
17559
17560                         unselectable : "on",
17561                         cn : [
17562                             {
17563                                 cls: 'fc-event-inner',
17564                                 cn : [
17565     //                                {
17566     //                                  tag:'span',
17567     //                                  cls: 'fc-event-time',
17568     //                                  html : cells.length > 1 ? '' : ev.time
17569     //                                },
17570                                     {
17571                                       tag:'span',
17572                                       cls: 'fc-event-title',
17573                                       html : String.format('{0}', ev.title)
17574                                     }
17575
17576
17577                                 ]
17578                             },
17579                             {
17580                                 cls: 'ui-resizable-handle ui-resizable-e',
17581                                 html : '&nbsp;&nbsp;&nbsp'
17582                             }
17583
17584                         ]
17585                     };
17586
17587                     if (i == 0) {
17588                         cfg.cls += ' fc-event-start';
17589                     }
17590                     if ((i+1) == rows.length) {
17591                         cfg.cls += ' fc-event-end';
17592                     }
17593
17594                     var ctr = _this.el.select('.fc-event-container',true).first();
17595                     var cg = ctr.createChild(cfg);
17596
17597                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17598                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17599
17600                     var r = (c.more.length) ? 1 : 0;
17601                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17602                     cg.setWidth(ebox.right - sbox.x -2);
17603
17604                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17605                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17606                     cg.on('click', _this.onEventClick, _this, ev);
17607
17608                     ev.els.push(cg);
17609                     
17610                 }
17611                 
17612             }
17613             
17614             
17615             if(c.more.length){
17616                 var  cfg = {
17617                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17618                     style : 'position: absolute',
17619                     unselectable : "on",
17620                     cn : [
17621                         {
17622                             cls: 'fc-event-inner',
17623                             cn : [
17624                                 {
17625                                   tag:'span',
17626                                   cls: 'fc-event-title',
17627                                   html : 'More'
17628                                 }
17629
17630
17631                             ]
17632                         },
17633                         {
17634                             cls: 'ui-resizable-handle ui-resizable-e',
17635                             html : '&nbsp;&nbsp;&nbsp'
17636                         }
17637
17638                     ]
17639                 };
17640
17641                 var ctr = _this.el.select('.fc-event-container',true).first();
17642                 var cg = ctr.createChild(cfg);
17643
17644                 var sbox = c.select('.fc-day-content',true).first().getBox();
17645                 var ebox = c.select('.fc-day-content',true).first().getBox();
17646                 //Roo.log(cg);
17647                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17648                 cg.setWidth(ebox.right - sbox.x -2);
17649
17650                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17651                 
17652             }
17653             
17654         });
17655         
17656         
17657         
17658     },
17659     
17660     onEventEnter: function (e, el,event,d) {
17661         this.fireEvent('evententer', this, el, event);
17662     },
17663     
17664     onEventLeave: function (e, el,event,d) {
17665         this.fireEvent('eventleave', this, el, event);
17666     },
17667     
17668     onEventClick: function (e, el,event,d) {
17669         this.fireEvent('eventclick', this, el, event);
17670     },
17671     
17672     onMonthChange: function () {
17673         this.store.load();
17674     },
17675     
17676     onMoreEventClick: function(e, el, more)
17677     {
17678         var _this = this;
17679         
17680         this.calpopover.placement = 'right';
17681         this.calpopover.setTitle('More');
17682         
17683         this.calpopover.setContent('');
17684         
17685         var ctr = this.calpopover.el.select('.popover-content', true).first();
17686         
17687         Roo.each(more, function(m){
17688             var cfg = {
17689                 cls : 'fc-event-hori fc-event-draggable',
17690                 html : m.title
17691             };
17692             var cg = ctr.createChild(cfg);
17693             
17694             cg.on('click', _this.onEventClick, _this, m);
17695         });
17696         
17697         this.calpopover.show(el);
17698         
17699         
17700     },
17701     
17702     onLoad: function () 
17703     {   
17704         this.calevents = [];
17705         var cal = this;
17706         
17707         if(this.store.getCount() > 0){
17708             this.store.data.each(function(d){
17709                cal.addItem({
17710                     id : d.data.id,
17711                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17712                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17713                     time : d.data.start_time,
17714                     title : d.data.title,
17715                     description : d.data.description,
17716                     venue : d.data.venue
17717                 });
17718             });
17719         }
17720         
17721         this.renderEvents();
17722         
17723         if(this.calevents.length && this.loadMask){
17724             this.maskEl.hide();
17725         }
17726     },
17727     
17728     onBeforeLoad: function()
17729     {
17730         this.clearEvents();
17731         if(this.loadMask){
17732             this.maskEl.show();
17733         }
17734     }
17735 });
17736
17737  
17738  /*
17739  * - LGPL
17740  *
17741  * element
17742  * 
17743  */
17744
17745 /**
17746  * @class Roo.bootstrap.Popover
17747  * @extends Roo.bootstrap.Component
17748  * Bootstrap Popover class
17749  * @cfg {String} html contents of the popover   (or false to use children..)
17750  * @cfg {String} title of popover (or false to hide)
17751  * @cfg {String} placement how it is placed
17752  * @cfg {String} trigger click || hover (or false to trigger manually)
17753  * @cfg {String} over what (parent or false to trigger manually.)
17754  * @cfg {Number} delay - delay before showing
17755  
17756  * @constructor
17757  * Create a new Popover
17758  * @param {Object} config The config object
17759  */
17760
17761 Roo.bootstrap.Popover = function(config){
17762     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17763     
17764     this.addEvents({
17765         // raw events
17766          /**
17767          * @event show
17768          * After the popover show
17769          * 
17770          * @param {Roo.bootstrap.Popover} this
17771          */
17772         "show" : true,
17773         /**
17774          * @event hide
17775          * After the popover hide
17776          * 
17777          * @param {Roo.bootstrap.Popover} this
17778          */
17779         "hide" : true
17780     });
17781 };
17782
17783 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17784     
17785     title: 'Fill in a title',
17786     html: false,
17787     
17788     placement : 'right',
17789     trigger : 'hover', // hover
17790     
17791     delay : 0,
17792     
17793     over: 'parent',
17794     
17795     can_build_overlaid : false,
17796     
17797     getChildContainer : function()
17798     {
17799         return this.el.select('.popover-content',true).first();
17800     },
17801     
17802     getAutoCreate : function(){
17803          
17804         var cfg = {
17805            cls : 'popover roo-dynamic',
17806            style: 'display:block',
17807            cn : [
17808                 {
17809                     cls : 'arrow'
17810                 },
17811                 {
17812                     cls : 'popover-inner',
17813                     cn : [
17814                         {
17815                             tag: 'h3',
17816                             cls: 'popover-title popover-header',
17817                             html : this.title
17818                         },
17819                         {
17820                             cls : 'popover-content popover-body',
17821                             html : this.html
17822                         }
17823                     ]
17824                     
17825                 }
17826            ]
17827         };
17828         
17829         return cfg;
17830     },
17831     setTitle: function(str)
17832     {
17833         this.title = str;
17834         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17835     },
17836     setContent: function(str)
17837     {
17838         this.html = str;
17839         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17840     },
17841     // as it get's added to the bottom of the page.
17842     onRender : function(ct, position)
17843     {
17844         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17845         if(!this.el){
17846             var cfg = Roo.apply({},  this.getAutoCreate());
17847             cfg.id = Roo.id();
17848             
17849             if (this.cls) {
17850                 cfg.cls += ' ' + this.cls;
17851             }
17852             if (this.style) {
17853                 cfg.style = this.style;
17854             }
17855             //Roo.log("adding to ");
17856             this.el = Roo.get(document.body).createChild(cfg, position);
17857 //            Roo.log(this.el);
17858         }
17859         this.initEvents();
17860     },
17861     
17862     initEvents : function()
17863     {
17864         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17865         this.el.enableDisplayMode('block');
17866         this.el.hide();
17867         if (this.over === false) {
17868             return; 
17869         }
17870         if (this.triggers === false) {
17871             return;
17872         }
17873         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17874         var triggers = this.trigger ? this.trigger.split(' ') : [];
17875         Roo.each(triggers, function(trigger) {
17876         
17877             if (trigger == 'click') {
17878                 on_el.on('click', this.toggle, this);
17879             } else if (trigger != 'manual') {
17880                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17881                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17882       
17883                 on_el.on(eventIn  ,this.enter, this);
17884                 on_el.on(eventOut, this.leave, this);
17885             }
17886         }, this);
17887         
17888     },
17889     
17890     
17891     // private
17892     timeout : null,
17893     hoverState : null,
17894     
17895     toggle : function () {
17896         this.hoverState == 'in' ? this.leave() : this.enter();
17897     },
17898     
17899     enter : function () {
17900         
17901         clearTimeout(this.timeout);
17902     
17903         this.hoverState = 'in';
17904     
17905         if (!this.delay || !this.delay.show) {
17906             this.show();
17907             return;
17908         }
17909         var _t = this;
17910         this.timeout = setTimeout(function () {
17911             if (_t.hoverState == 'in') {
17912                 _t.show();
17913             }
17914         }, this.delay.show)
17915     },
17916     
17917     leave : function() {
17918         clearTimeout(this.timeout);
17919     
17920         this.hoverState = 'out';
17921     
17922         if (!this.delay || !this.delay.hide) {
17923             this.hide();
17924             return;
17925         }
17926         var _t = this;
17927         this.timeout = setTimeout(function () {
17928             if (_t.hoverState == 'out') {
17929                 _t.hide();
17930             }
17931         }, this.delay.hide)
17932     },
17933     
17934     show : function (on_el)
17935     {
17936         if (!on_el) {
17937             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17938         }
17939         
17940         // set content.
17941         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17942         if (this.html !== false) {
17943             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17944         }
17945         this.el.removeClass([
17946             'fade','top','bottom', 'left', 'right','in',
17947             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
17948         ]);
17949         if (!this.title.length) {
17950             this.el.select('.popover-title',true).hide();
17951         }
17952         
17953         var placement = typeof this.placement == 'function' ?
17954             this.placement.call(this, this.el, on_el) :
17955             this.placement;
17956             
17957         var autoToken = /\s?auto?\s?/i;
17958         var autoPlace = autoToken.test(placement);
17959         if (autoPlace) {
17960             placement = placement.replace(autoToken, '') || 'top';
17961         }
17962         
17963         //this.el.detach()
17964         //this.el.setXY([0,0]);
17965         this.el.show();
17966         this.el.dom.style.display='block';
17967         this.el.addClass(placement);
17968         
17969         //this.el.appendTo(on_el);
17970         
17971         var p = this.getPosition();
17972         var box = this.el.getBox();
17973         
17974         if (autoPlace) {
17975             // fixme..
17976         }
17977         var align = Roo.bootstrap.Popover.alignment[placement];
17978         
17979 //        Roo.log(align);
17980         this.el.alignTo(on_el, align[0],align[1]);
17981         //var arrow = this.el.select('.arrow',true).first();
17982         //arrow.set(align[2], 
17983         
17984         this.el.addClass('in');
17985         
17986         
17987         if (this.el.hasClass('fade')) {
17988             // fade it?
17989         }
17990         
17991         this.hoverState = 'in';
17992         
17993         this.fireEvent('show', this);
17994         
17995     },
17996     hide : function()
17997     {
17998         this.el.setXY([0,0]);
17999         this.el.removeClass('in');
18000         this.el.hide();
18001         this.hoverState = null;
18002         
18003         this.fireEvent('hide', this);
18004     }
18005     
18006 });
18007
18008 Roo.bootstrap.Popover.alignment = {
18009     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
18010     'right' : ['l-r', [10,0], 'left bs-popover-left'],
18011     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
18012     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
18013 };
18014
18015  /*
18016  * - LGPL
18017  *
18018  * Progress
18019  * 
18020  */
18021
18022 /**
18023  * @class Roo.bootstrap.Progress
18024  * @extends Roo.bootstrap.Component
18025  * Bootstrap Progress class
18026  * @cfg {Boolean} striped striped of the progress bar
18027  * @cfg {Boolean} active animated of the progress bar
18028  * 
18029  * 
18030  * @constructor
18031  * Create a new Progress
18032  * @param {Object} config The config object
18033  */
18034
18035 Roo.bootstrap.Progress = function(config){
18036     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
18037 };
18038
18039 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
18040     
18041     striped : false,
18042     active: false,
18043     
18044     getAutoCreate : function(){
18045         var cfg = {
18046             tag: 'div',
18047             cls: 'progress'
18048         };
18049         
18050         
18051         if(this.striped){
18052             cfg.cls += ' progress-striped';
18053         }
18054       
18055         if(this.active){
18056             cfg.cls += ' active';
18057         }
18058         
18059         
18060         return cfg;
18061     }
18062    
18063 });
18064
18065  
18066
18067  /*
18068  * - LGPL
18069  *
18070  * ProgressBar
18071  * 
18072  */
18073
18074 /**
18075  * @class Roo.bootstrap.ProgressBar
18076  * @extends Roo.bootstrap.Component
18077  * Bootstrap ProgressBar class
18078  * @cfg {Number} aria_valuenow aria-value now
18079  * @cfg {Number} aria_valuemin aria-value min
18080  * @cfg {Number} aria_valuemax aria-value max
18081  * @cfg {String} label label for the progress bar
18082  * @cfg {String} panel (success | info | warning | danger )
18083  * @cfg {String} role role of the progress bar
18084  * @cfg {String} sr_only text
18085  * 
18086  * 
18087  * @constructor
18088  * Create a new ProgressBar
18089  * @param {Object} config The config object
18090  */
18091
18092 Roo.bootstrap.ProgressBar = function(config){
18093     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
18094 };
18095
18096 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
18097     
18098     aria_valuenow : 0,
18099     aria_valuemin : 0,
18100     aria_valuemax : 100,
18101     label : false,
18102     panel : false,
18103     role : false,
18104     sr_only: false,
18105     
18106     getAutoCreate : function()
18107     {
18108         
18109         var cfg = {
18110             tag: 'div',
18111             cls: 'progress-bar',
18112             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
18113         };
18114         
18115         if(this.sr_only){
18116             cfg.cn = {
18117                 tag: 'span',
18118                 cls: 'sr-only',
18119                 html: this.sr_only
18120             }
18121         }
18122         
18123         if(this.role){
18124             cfg.role = this.role;
18125         }
18126         
18127         if(this.aria_valuenow){
18128             cfg['aria-valuenow'] = this.aria_valuenow;
18129         }
18130         
18131         if(this.aria_valuemin){
18132             cfg['aria-valuemin'] = this.aria_valuemin;
18133         }
18134         
18135         if(this.aria_valuemax){
18136             cfg['aria-valuemax'] = this.aria_valuemax;
18137         }
18138         
18139         if(this.label && !this.sr_only){
18140             cfg.html = this.label;
18141         }
18142         
18143         if(this.panel){
18144             cfg.cls += ' progress-bar-' + this.panel;
18145         }
18146         
18147         return cfg;
18148     },
18149     
18150     update : function(aria_valuenow)
18151     {
18152         this.aria_valuenow = aria_valuenow;
18153         
18154         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
18155     }
18156    
18157 });
18158
18159  
18160
18161  /*
18162  * - LGPL
18163  *
18164  * column
18165  * 
18166  */
18167
18168 /**
18169  * @class Roo.bootstrap.TabGroup
18170  * @extends Roo.bootstrap.Column
18171  * Bootstrap Column class
18172  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
18173  * @cfg {Boolean} carousel true to make the group behave like a carousel
18174  * @cfg {Boolean} bullets show bullets for the panels
18175  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
18176  * @cfg {Number} timer auto slide timer .. default 0 millisecond
18177  * @cfg {Boolean} showarrow (true|false) show arrow default true
18178  * 
18179  * @constructor
18180  * Create a new TabGroup
18181  * @param {Object} config The config object
18182  */
18183
18184 Roo.bootstrap.TabGroup = function(config){
18185     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
18186     if (!this.navId) {
18187         this.navId = Roo.id();
18188     }
18189     this.tabs = [];
18190     Roo.bootstrap.TabGroup.register(this);
18191     
18192 };
18193
18194 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
18195     
18196     carousel : false,
18197     transition : false,
18198     bullets : 0,
18199     timer : 0,
18200     autoslide : false,
18201     slideFn : false,
18202     slideOnTouch : false,
18203     showarrow : true,
18204     
18205     getAutoCreate : function()
18206     {
18207         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
18208         
18209         cfg.cls += ' tab-content';
18210         
18211         if (this.carousel) {
18212             cfg.cls += ' carousel slide';
18213             
18214             cfg.cn = [{
18215                cls : 'carousel-inner',
18216                cn : []
18217             }];
18218         
18219             if(this.bullets  && !Roo.isTouch){
18220                 
18221                 var bullets = {
18222                     cls : 'carousel-bullets',
18223                     cn : []
18224                 };
18225                
18226                 if(this.bullets_cls){
18227                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
18228                 }
18229                 
18230                 bullets.cn.push({
18231                     cls : 'clear'
18232                 });
18233                 
18234                 cfg.cn[0].cn.push(bullets);
18235             }
18236             
18237             if(this.showarrow){
18238                 cfg.cn[0].cn.push({
18239                     tag : 'div',
18240                     class : 'carousel-arrow',
18241                     cn : [
18242                         {
18243                             tag : 'div',
18244                             class : 'carousel-prev',
18245                             cn : [
18246                                 {
18247                                     tag : 'i',
18248                                     class : 'fa fa-chevron-left'
18249                                 }
18250                             ]
18251                         },
18252                         {
18253                             tag : 'div',
18254                             class : 'carousel-next',
18255                             cn : [
18256                                 {
18257                                     tag : 'i',
18258                                     class : 'fa fa-chevron-right'
18259                                 }
18260                             ]
18261                         }
18262                     ]
18263                 });
18264             }
18265             
18266         }
18267         
18268         return cfg;
18269     },
18270     
18271     initEvents:  function()
18272     {
18273 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
18274 //            this.el.on("touchstart", this.onTouchStart, this);
18275 //        }
18276         
18277         if(this.autoslide){
18278             var _this = this;
18279             
18280             this.slideFn = window.setInterval(function() {
18281                 _this.showPanelNext();
18282             }, this.timer);
18283         }
18284         
18285         if(this.showarrow){
18286             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
18287             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
18288         }
18289         
18290         
18291     },
18292     
18293 //    onTouchStart : function(e, el, o)
18294 //    {
18295 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
18296 //            return;
18297 //        }
18298 //        
18299 //        this.showPanelNext();
18300 //    },
18301     
18302     
18303     getChildContainer : function()
18304     {
18305         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
18306     },
18307     
18308     /**
18309     * register a Navigation item
18310     * @param {Roo.bootstrap.NavItem} the navitem to add
18311     */
18312     register : function(item)
18313     {
18314         this.tabs.push( item);
18315         item.navId = this.navId; // not really needed..
18316         this.addBullet();
18317     
18318     },
18319     
18320     getActivePanel : function()
18321     {
18322         var r = false;
18323         Roo.each(this.tabs, function(t) {
18324             if (t.active) {
18325                 r = t;
18326                 return false;
18327             }
18328             return null;
18329         });
18330         return r;
18331         
18332     },
18333     getPanelByName : function(n)
18334     {
18335         var r = false;
18336         Roo.each(this.tabs, function(t) {
18337             if (t.tabId == n) {
18338                 r = t;
18339                 return false;
18340             }
18341             return null;
18342         });
18343         return r;
18344     },
18345     indexOfPanel : function(p)
18346     {
18347         var r = false;
18348         Roo.each(this.tabs, function(t,i) {
18349             if (t.tabId == p.tabId) {
18350                 r = i;
18351                 return false;
18352             }
18353             return null;
18354         });
18355         return r;
18356     },
18357     /**
18358      * show a specific panel
18359      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
18360      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
18361      */
18362     showPanel : function (pan)
18363     {
18364         if(this.transition || typeof(pan) == 'undefined'){
18365             Roo.log("waiting for the transitionend");
18366             return false;
18367         }
18368         
18369         if (typeof(pan) == 'number') {
18370             pan = this.tabs[pan];
18371         }
18372         
18373         if (typeof(pan) == 'string') {
18374             pan = this.getPanelByName(pan);
18375         }
18376         
18377         var cur = this.getActivePanel();
18378         
18379         if(!pan || !cur){
18380             Roo.log('pan or acitve pan is undefined');
18381             return false;
18382         }
18383         
18384         if (pan.tabId == this.getActivePanel().tabId) {
18385             return true;
18386         }
18387         
18388         if (false === cur.fireEvent('beforedeactivate')) {
18389             return false;
18390         }
18391         
18392         if(this.bullets > 0 && !Roo.isTouch){
18393             this.setActiveBullet(this.indexOfPanel(pan));
18394         }
18395         
18396         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
18397             
18398             //class="carousel-item carousel-item-next carousel-item-left"
18399             
18400             this.transition = true;
18401             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
18402             var lr = dir == 'next' ? 'left' : 'right';
18403             pan.el.addClass(dir); // or prev
18404             pan.el.addClass('carousel-item-' + dir); // or prev
18405             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
18406             cur.el.addClass(lr); // or right
18407             pan.el.addClass(lr);
18408             cur.el.addClass('carousel-item-' +lr); // or right
18409             pan.el.addClass('carousel-item-' +lr);
18410             
18411             
18412             var _this = this;
18413             cur.el.on('transitionend', function() {
18414                 Roo.log("trans end?");
18415                 
18416                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
18417                 pan.setActive(true);
18418                 
18419                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
18420                 cur.setActive(false);
18421                 
18422                 _this.transition = false;
18423                 
18424             }, this, { single:  true } );
18425             
18426             return true;
18427         }
18428         
18429         cur.setActive(false);
18430         pan.setActive(true);
18431         
18432         return true;
18433         
18434     },
18435     showPanelNext : function()
18436     {
18437         var i = this.indexOfPanel(this.getActivePanel());
18438         
18439         if (i >= this.tabs.length - 1 && !this.autoslide) {
18440             return;
18441         }
18442         
18443         if (i >= this.tabs.length - 1 && this.autoslide) {
18444             i = -1;
18445         }
18446         
18447         this.showPanel(this.tabs[i+1]);
18448     },
18449     
18450     showPanelPrev : function()
18451     {
18452         var i = this.indexOfPanel(this.getActivePanel());
18453         
18454         if (i  < 1 && !this.autoslide) {
18455             return;
18456         }
18457         
18458         if (i < 1 && this.autoslide) {
18459             i = this.tabs.length;
18460         }
18461         
18462         this.showPanel(this.tabs[i-1]);
18463     },
18464     
18465     
18466     addBullet: function()
18467     {
18468         if(!this.bullets || Roo.isTouch){
18469             return;
18470         }
18471         var ctr = this.el.select('.carousel-bullets',true).first();
18472         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
18473         var bullet = ctr.createChild({
18474             cls : 'bullet bullet-' + i
18475         },ctr.dom.lastChild);
18476         
18477         
18478         var _this = this;
18479         
18480         bullet.on('click', (function(e, el, o, ii, t){
18481
18482             e.preventDefault();
18483
18484             this.showPanel(ii);
18485
18486             if(this.autoslide && this.slideFn){
18487                 clearInterval(this.slideFn);
18488                 this.slideFn = window.setInterval(function() {
18489                     _this.showPanelNext();
18490                 }, this.timer);
18491             }
18492
18493         }).createDelegate(this, [i, bullet], true));
18494                 
18495         
18496     },
18497      
18498     setActiveBullet : function(i)
18499     {
18500         if(Roo.isTouch){
18501             return;
18502         }
18503         
18504         Roo.each(this.el.select('.bullet', true).elements, function(el){
18505             el.removeClass('selected');
18506         });
18507
18508         var bullet = this.el.select('.bullet-' + i, true).first();
18509         
18510         if(!bullet){
18511             return;
18512         }
18513         
18514         bullet.addClass('selected');
18515     }
18516     
18517     
18518   
18519 });
18520
18521  
18522
18523  
18524  
18525 Roo.apply(Roo.bootstrap.TabGroup, {
18526     
18527     groups: {},
18528      /**
18529     * register a Navigation Group
18530     * @param {Roo.bootstrap.NavGroup} the navgroup to add
18531     */
18532     register : function(navgrp)
18533     {
18534         this.groups[navgrp.navId] = navgrp;
18535         
18536     },
18537     /**
18538     * fetch a Navigation Group based on the navigation ID
18539     * if one does not exist , it will get created.
18540     * @param {string} the navgroup to add
18541     * @returns {Roo.bootstrap.NavGroup} the navgroup 
18542     */
18543     get: function(navId) {
18544         if (typeof(this.groups[navId]) == 'undefined') {
18545             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
18546         }
18547         return this.groups[navId] ;
18548     }
18549     
18550     
18551     
18552 });
18553
18554  /*
18555  * - LGPL
18556  *
18557  * TabPanel
18558  * 
18559  */
18560
18561 /**
18562  * @class Roo.bootstrap.TabPanel
18563  * @extends Roo.bootstrap.Component
18564  * Bootstrap TabPanel class
18565  * @cfg {Boolean} active panel active
18566  * @cfg {String} html panel content
18567  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
18568  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
18569  * @cfg {String} href click to link..
18570  * 
18571  * 
18572  * @constructor
18573  * Create a new TabPanel
18574  * @param {Object} config The config object
18575  */
18576
18577 Roo.bootstrap.TabPanel = function(config){
18578     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
18579     this.addEvents({
18580         /**
18581              * @event changed
18582              * Fires when the active status changes
18583              * @param {Roo.bootstrap.TabPanel} this
18584              * @param {Boolean} state the new state
18585             
18586          */
18587         'changed': true,
18588         /**
18589              * @event beforedeactivate
18590              * Fires before a tab is de-activated - can be used to do validation on a form.
18591              * @param {Roo.bootstrap.TabPanel} this
18592              * @return {Boolean} false if there is an error
18593             
18594          */
18595         'beforedeactivate': true
18596      });
18597     
18598     this.tabId = this.tabId || Roo.id();
18599   
18600 };
18601
18602 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18603     
18604     active: false,
18605     html: false,
18606     tabId: false,
18607     navId : false,
18608     href : '',
18609     
18610     getAutoCreate : function(){
18611         
18612         
18613         var cfg = {
18614             tag: 'div',
18615             // item is needed for carousel - not sure if it has any effect otherwise
18616             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18617             html: this.html || ''
18618         };
18619         
18620         if(this.active){
18621             cfg.cls += ' active';
18622         }
18623         
18624         if(this.tabId){
18625             cfg.tabId = this.tabId;
18626         }
18627         
18628         
18629         
18630         return cfg;
18631     },
18632     
18633     initEvents:  function()
18634     {
18635         var p = this.parent();
18636         
18637         this.navId = this.navId || p.navId;
18638         
18639         if (typeof(this.navId) != 'undefined') {
18640             // not really needed.. but just in case.. parent should be a NavGroup.
18641             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18642             
18643             tg.register(this);
18644             
18645             var i = tg.tabs.length - 1;
18646             
18647             if(this.active && tg.bullets > 0 && i < tg.bullets){
18648                 tg.setActiveBullet(i);
18649             }
18650         }
18651         
18652         this.el.on('click', this.onClick, this);
18653         
18654         if(Roo.isTouch){
18655             this.el.on("touchstart", this.onTouchStart, this);
18656             this.el.on("touchmove", this.onTouchMove, this);
18657             this.el.on("touchend", this.onTouchEnd, this);
18658         }
18659         
18660     },
18661     
18662     onRender : function(ct, position)
18663     {
18664         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18665     },
18666     
18667     setActive : function(state)
18668     {
18669         Roo.log("panel - set active " + this.tabId + "=" + state);
18670         
18671         this.active = state;
18672         if (!state) {
18673             this.el.removeClass('active');
18674             
18675         } else  if (!this.el.hasClass('active')) {
18676             this.el.addClass('active');
18677         }
18678         
18679         this.fireEvent('changed', this, state);
18680     },
18681     
18682     onClick : function(e)
18683     {
18684         e.preventDefault();
18685         
18686         if(!this.href.length){
18687             return;
18688         }
18689         
18690         window.location.href = this.href;
18691     },
18692     
18693     startX : 0,
18694     startY : 0,
18695     endX : 0,
18696     endY : 0,
18697     swiping : false,
18698     
18699     onTouchStart : function(e)
18700     {
18701         this.swiping = false;
18702         
18703         this.startX = e.browserEvent.touches[0].clientX;
18704         this.startY = e.browserEvent.touches[0].clientY;
18705     },
18706     
18707     onTouchMove : function(e)
18708     {
18709         this.swiping = true;
18710         
18711         this.endX = e.browserEvent.touches[0].clientX;
18712         this.endY = e.browserEvent.touches[0].clientY;
18713     },
18714     
18715     onTouchEnd : function(e)
18716     {
18717         if(!this.swiping){
18718             this.onClick(e);
18719             return;
18720         }
18721         
18722         var tabGroup = this.parent();
18723         
18724         if(this.endX > this.startX){ // swiping right
18725             tabGroup.showPanelPrev();
18726             return;
18727         }
18728         
18729         if(this.startX > this.endX){ // swiping left
18730             tabGroup.showPanelNext();
18731             return;
18732         }
18733     }
18734     
18735     
18736 });
18737  
18738
18739  
18740
18741  /*
18742  * - LGPL
18743  *
18744  * DateField
18745  * 
18746  */
18747
18748 /**
18749  * @class Roo.bootstrap.DateField
18750  * @extends Roo.bootstrap.Input
18751  * Bootstrap DateField class
18752  * @cfg {Number} weekStart default 0
18753  * @cfg {String} viewMode default empty, (months|years)
18754  * @cfg {String} minViewMode default empty, (months|years)
18755  * @cfg {Number} startDate default -Infinity
18756  * @cfg {Number} endDate default Infinity
18757  * @cfg {Boolean} todayHighlight default false
18758  * @cfg {Boolean} todayBtn default false
18759  * @cfg {Boolean} calendarWeeks default false
18760  * @cfg {Object} daysOfWeekDisabled default empty
18761  * @cfg {Boolean} singleMode default false (true | false)
18762  * 
18763  * @cfg {Boolean} keyboardNavigation default true
18764  * @cfg {String} language default en
18765  * 
18766  * @constructor
18767  * Create a new DateField
18768  * @param {Object} config The config object
18769  */
18770
18771 Roo.bootstrap.DateField = function(config){
18772     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18773      this.addEvents({
18774             /**
18775              * @event show
18776              * Fires when this field show.
18777              * @param {Roo.bootstrap.DateField} this
18778              * @param {Mixed} date The date value
18779              */
18780             show : true,
18781             /**
18782              * @event show
18783              * Fires when this field hide.
18784              * @param {Roo.bootstrap.DateField} this
18785              * @param {Mixed} date The date value
18786              */
18787             hide : true,
18788             /**
18789              * @event select
18790              * Fires when select a date.
18791              * @param {Roo.bootstrap.DateField} this
18792              * @param {Mixed} date The date value
18793              */
18794             select : true,
18795             /**
18796              * @event beforeselect
18797              * Fires when before select a date.
18798              * @param {Roo.bootstrap.DateField} this
18799              * @param {Mixed} date The date value
18800              */
18801             beforeselect : true
18802         });
18803 };
18804
18805 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18806     
18807     /**
18808      * @cfg {String} format
18809      * The default date format string which can be overriden for localization support.  The format must be
18810      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18811      */
18812     format : "m/d/y",
18813     /**
18814      * @cfg {String} altFormats
18815      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18816      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18817      */
18818     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18819     
18820     weekStart : 0,
18821     
18822     viewMode : '',
18823     
18824     minViewMode : '',
18825     
18826     todayHighlight : false,
18827     
18828     todayBtn: false,
18829     
18830     language: 'en',
18831     
18832     keyboardNavigation: true,
18833     
18834     calendarWeeks: false,
18835     
18836     startDate: -Infinity,
18837     
18838     endDate: Infinity,
18839     
18840     daysOfWeekDisabled: [],
18841     
18842     _events: [],
18843     
18844     singleMode : false,
18845     
18846     UTCDate: function()
18847     {
18848         return new Date(Date.UTC.apply(Date, arguments));
18849     },
18850     
18851     UTCToday: function()
18852     {
18853         var today = new Date();
18854         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18855     },
18856     
18857     getDate: function() {
18858             var d = this.getUTCDate();
18859             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18860     },
18861     
18862     getUTCDate: function() {
18863             return this.date;
18864     },
18865     
18866     setDate: function(d) {
18867             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18868     },
18869     
18870     setUTCDate: function(d) {
18871             this.date = d;
18872             this.setValue(this.formatDate(this.date));
18873     },
18874         
18875     onRender: function(ct, position)
18876     {
18877         
18878         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18879         
18880         this.language = this.language || 'en';
18881         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18882         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18883         
18884         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18885         this.format = this.format || 'm/d/y';
18886         this.isInline = false;
18887         this.isInput = true;
18888         this.component = this.el.select('.add-on', true).first() || false;
18889         this.component = (this.component && this.component.length === 0) ? false : this.component;
18890         this.hasInput = this.component && this.inputEl().length;
18891         
18892         if (typeof(this.minViewMode === 'string')) {
18893             switch (this.minViewMode) {
18894                 case 'months':
18895                     this.minViewMode = 1;
18896                     break;
18897                 case 'years':
18898                     this.minViewMode = 2;
18899                     break;
18900                 default:
18901                     this.minViewMode = 0;
18902                     break;
18903             }
18904         }
18905         
18906         if (typeof(this.viewMode === 'string')) {
18907             switch (this.viewMode) {
18908                 case 'months':
18909                     this.viewMode = 1;
18910                     break;
18911                 case 'years':
18912                     this.viewMode = 2;
18913                     break;
18914                 default:
18915                     this.viewMode = 0;
18916                     break;
18917             }
18918         }
18919                 
18920         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18921         
18922 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18923         
18924         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18925         
18926         this.picker().on('mousedown', this.onMousedown, this);
18927         this.picker().on('click', this.onClick, this);
18928         
18929         this.picker().addClass('datepicker-dropdown');
18930         
18931         this.startViewMode = this.viewMode;
18932         
18933         if(this.singleMode){
18934             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18935                 v.setVisibilityMode(Roo.Element.DISPLAY);
18936                 v.hide();
18937             });
18938             
18939             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18940                 v.setStyle('width', '189px');
18941             });
18942         }
18943         
18944         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18945             if(!this.calendarWeeks){
18946                 v.remove();
18947                 return;
18948             }
18949             
18950             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18951             v.attr('colspan', function(i, val){
18952                 return parseInt(val) + 1;
18953             });
18954         });
18955                         
18956         
18957         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18958         
18959         this.setStartDate(this.startDate);
18960         this.setEndDate(this.endDate);
18961         
18962         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18963         
18964         this.fillDow();
18965         this.fillMonths();
18966         this.update();
18967         this.showMode();
18968         
18969         if(this.isInline) {
18970             this.showPopup();
18971         }
18972     },
18973     
18974     picker : function()
18975     {
18976         return this.pickerEl;
18977 //        return this.el.select('.datepicker', true).first();
18978     },
18979     
18980     fillDow: function()
18981     {
18982         var dowCnt = this.weekStart;
18983         
18984         var dow = {
18985             tag: 'tr',
18986             cn: [
18987                 
18988             ]
18989         };
18990         
18991         if(this.calendarWeeks){
18992             dow.cn.push({
18993                 tag: 'th',
18994                 cls: 'cw',
18995                 html: '&nbsp;'
18996             })
18997         }
18998         
18999         while (dowCnt < this.weekStart + 7) {
19000             dow.cn.push({
19001                 tag: 'th',
19002                 cls: 'dow',
19003                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
19004             });
19005         }
19006         
19007         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
19008     },
19009     
19010     fillMonths: function()
19011     {    
19012         var i = 0;
19013         var months = this.picker().select('>.datepicker-months td', true).first();
19014         
19015         months.dom.innerHTML = '';
19016         
19017         while (i < 12) {
19018             var month = {
19019                 tag: 'span',
19020                 cls: 'month',
19021                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
19022             };
19023             
19024             months.createChild(month);
19025         }
19026         
19027     },
19028     
19029     update: function()
19030     {
19031         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;
19032         
19033         if (this.date < this.startDate) {
19034             this.viewDate = new Date(this.startDate);
19035         } else if (this.date > this.endDate) {
19036             this.viewDate = new Date(this.endDate);
19037         } else {
19038             this.viewDate = new Date(this.date);
19039         }
19040         
19041         this.fill();
19042     },
19043     
19044     fill: function() 
19045     {
19046         var d = new Date(this.viewDate),
19047                 year = d.getUTCFullYear(),
19048                 month = d.getUTCMonth(),
19049                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
19050                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
19051                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
19052                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
19053                 currentDate = this.date && this.date.valueOf(),
19054                 today = this.UTCToday();
19055         
19056         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
19057         
19058 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
19059         
19060 //        this.picker.select('>tfoot th.today').
19061 //                                              .text(dates[this.language].today)
19062 //                                              .toggle(this.todayBtn !== false);
19063     
19064         this.updateNavArrows();
19065         this.fillMonths();
19066                                                 
19067         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
19068         
19069         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
19070          
19071         prevMonth.setUTCDate(day);
19072         
19073         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
19074         
19075         var nextMonth = new Date(prevMonth);
19076         
19077         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
19078         
19079         nextMonth = nextMonth.valueOf();
19080         
19081         var fillMonths = false;
19082         
19083         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
19084         
19085         while(prevMonth.valueOf() <= nextMonth) {
19086             var clsName = '';
19087             
19088             if (prevMonth.getUTCDay() === this.weekStart) {
19089                 if(fillMonths){
19090                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
19091                 }
19092                     
19093                 fillMonths = {
19094                     tag: 'tr',
19095                     cn: []
19096                 };
19097                 
19098                 if(this.calendarWeeks){
19099                     // ISO 8601: First week contains first thursday.
19100                     // ISO also states week starts on Monday, but we can be more abstract here.
19101                     var
19102                     // Start of current week: based on weekstart/current date
19103                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
19104                     // Thursday of this week
19105                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
19106                     // First Thursday of year, year from thursday
19107                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
19108                     // Calendar week: ms between thursdays, div ms per day, div 7 days
19109                     calWeek =  (th - yth) / 864e5 / 7 + 1;
19110                     
19111                     fillMonths.cn.push({
19112                         tag: 'td',
19113                         cls: 'cw',
19114                         html: calWeek
19115                     });
19116                 }
19117             }
19118             
19119             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
19120                 clsName += ' old';
19121             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
19122                 clsName += ' new';
19123             }
19124             if (this.todayHighlight &&
19125                 prevMonth.getUTCFullYear() == today.getFullYear() &&
19126                 prevMonth.getUTCMonth() == today.getMonth() &&
19127                 prevMonth.getUTCDate() == today.getDate()) {
19128                 clsName += ' today';
19129             }
19130             
19131             if (currentDate && prevMonth.valueOf() === currentDate) {
19132                 clsName += ' active';
19133             }
19134             
19135             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
19136                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
19137                     clsName += ' disabled';
19138             }
19139             
19140             fillMonths.cn.push({
19141                 tag: 'td',
19142                 cls: 'day ' + clsName,
19143                 html: prevMonth.getDate()
19144             });
19145             
19146             prevMonth.setDate(prevMonth.getDate()+1);
19147         }
19148           
19149         var currentYear = this.date && this.date.getUTCFullYear();
19150         var currentMonth = this.date && this.date.getUTCMonth();
19151         
19152         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
19153         
19154         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
19155             v.removeClass('active');
19156             
19157             if(currentYear === year && k === currentMonth){
19158                 v.addClass('active');
19159             }
19160             
19161             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
19162                 v.addClass('disabled');
19163             }
19164             
19165         });
19166         
19167         
19168         year = parseInt(year/10, 10) * 10;
19169         
19170         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
19171         
19172         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
19173         
19174         year -= 1;
19175         for (var i = -1; i < 11; i++) {
19176             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
19177                 tag: 'span',
19178                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
19179                 html: year
19180             });
19181             
19182             year += 1;
19183         }
19184     },
19185     
19186     showMode: function(dir) 
19187     {
19188         if (dir) {
19189             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
19190         }
19191         
19192         Roo.each(this.picker().select('>div',true).elements, function(v){
19193             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19194             v.hide();
19195         });
19196         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
19197     },
19198     
19199     place: function()
19200     {
19201         if(this.isInline) {
19202             return;
19203         }
19204         
19205         this.picker().removeClass(['bottom', 'top']);
19206         
19207         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19208             /*
19209              * place to the top of element!
19210              *
19211              */
19212             
19213             this.picker().addClass('top');
19214             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19215             
19216             return;
19217         }
19218         
19219         this.picker().addClass('bottom');
19220         
19221         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19222     },
19223     
19224     parseDate : function(value)
19225     {
19226         if(!value || value instanceof Date){
19227             return value;
19228         }
19229         var v = Date.parseDate(value, this.format);
19230         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
19231             v = Date.parseDate(value, 'Y-m-d');
19232         }
19233         if(!v && this.altFormats){
19234             if(!this.altFormatsArray){
19235                 this.altFormatsArray = this.altFormats.split("|");
19236             }
19237             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
19238                 v = Date.parseDate(value, this.altFormatsArray[i]);
19239             }
19240         }
19241         return v;
19242     },
19243     
19244     formatDate : function(date, fmt)
19245     {   
19246         return (!date || !(date instanceof Date)) ?
19247         date : date.dateFormat(fmt || this.format);
19248     },
19249     
19250     onFocus : function()
19251     {
19252         Roo.bootstrap.DateField.superclass.onFocus.call(this);
19253         this.showPopup();
19254     },
19255     
19256     onBlur : function()
19257     {
19258         Roo.bootstrap.DateField.superclass.onBlur.call(this);
19259         
19260         var d = this.inputEl().getValue();
19261         
19262         this.setValue(d);
19263                 
19264         this.hidePopup();
19265     },
19266     
19267     showPopup : function()
19268     {
19269         this.picker().show();
19270         this.update();
19271         this.place();
19272         
19273         this.fireEvent('showpopup', this, this.date);
19274     },
19275     
19276     hidePopup : function()
19277     {
19278         if(this.isInline) {
19279             return;
19280         }
19281         this.picker().hide();
19282         this.viewMode = this.startViewMode;
19283         this.showMode();
19284         
19285         this.fireEvent('hidepopup', this, this.date);
19286         
19287     },
19288     
19289     onMousedown: function(e)
19290     {
19291         e.stopPropagation();
19292         e.preventDefault();
19293     },
19294     
19295     keyup: function(e)
19296     {
19297         Roo.bootstrap.DateField.superclass.keyup.call(this);
19298         this.update();
19299     },
19300
19301     setValue: function(v)
19302     {
19303         if(this.fireEvent('beforeselect', this, v) !== false){
19304             var d = new Date(this.parseDate(v) ).clearTime();
19305         
19306             if(isNaN(d.getTime())){
19307                 this.date = this.viewDate = '';
19308                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19309                 return;
19310             }
19311
19312             v = this.formatDate(d);
19313
19314             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
19315
19316             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
19317
19318             this.update();
19319
19320             this.fireEvent('select', this, this.date);
19321         }
19322     },
19323     
19324     getValue: function()
19325     {
19326         return this.formatDate(this.date);
19327     },
19328     
19329     fireKey: function(e)
19330     {
19331         if (!this.picker().isVisible()){
19332             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19333                 this.showPopup();
19334             }
19335             return;
19336         }
19337         
19338         var dateChanged = false,
19339         dir, day, month,
19340         newDate, newViewDate;
19341         
19342         switch(e.keyCode){
19343             case 27: // escape
19344                 this.hidePopup();
19345                 e.preventDefault();
19346                 break;
19347             case 37: // left
19348             case 39: // right
19349                 if (!this.keyboardNavigation) {
19350                     break;
19351                 }
19352                 dir = e.keyCode == 37 ? -1 : 1;
19353                 
19354                 if (e.ctrlKey){
19355                     newDate = this.moveYear(this.date, dir);
19356                     newViewDate = this.moveYear(this.viewDate, dir);
19357                 } else if (e.shiftKey){
19358                     newDate = this.moveMonth(this.date, dir);
19359                     newViewDate = this.moveMonth(this.viewDate, dir);
19360                 } else {
19361                     newDate = new Date(this.date);
19362                     newDate.setUTCDate(this.date.getUTCDate() + dir);
19363                     newViewDate = new Date(this.viewDate);
19364                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
19365                 }
19366                 if (this.dateWithinRange(newDate)){
19367                     this.date = newDate;
19368                     this.viewDate = newViewDate;
19369                     this.setValue(this.formatDate(this.date));
19370 //                    this.update();
19371                     e.preventDefault();
19372                     dateChanged = true;
19373                 }
19374                 break;
19375             case 38: // up
19376             case 40: // down
19377                 if (!this.keyboardNavigation) {
19378                     break;
19379                 }
19380                 dir = e.keyCode == 38 ? -1 : 1;
19381                 if (e.ctrlKey){
19382                     newDate = this.moveYear(this.date, dir);
19383                     newViewDate = this.moveYear(this.viewDate, dir);
19384                 } else if (e.shiftKey){
19385                     newDate = this.moveMonth(this.date, dir);
19386                     newViewDate = this.moveMonth(this.viewDate, dir);
19387                 } else {
19388                     newDate = new Date(this.date);
19389                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
19390                     newViewDate = new Date(this.viewDate);
19391                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
19392                 }
19393                 if (this.dateWithinRange(newDate)){
19394                     this.date = newDate;
19395                     this.viewDate = newViewDate;
19396                     this.setValue(this.formatDate(this.date));
19397 //                    this.update();
19398                     e.preventDefault();
19399                     dateChanged = true;
19400                 }
19401                 break;
19402             case 13: // enter
19403                 this.setValue(this.formatDate(this.date));
19404                 this.hidePopup();
19405                 e.preventDefault();
19406                 break;
19407             case 9: // tab
19408                 this.setValue(this.formatDate(this.date));
19409                 this.hidePopup();
19410                 break;
19411             case 16: // shift
19412             case 17: // ctrl
19413             case 18: // alt
19414                 break;
19415             default :
19416                 this.hidePopup();
19417                 
19418         }
19419     },
19420     
19421     
19422     onClick: function(e) 
19423     {
19424         e.stopPropagation();
19425         e.preventDefault();
19426         
19427         var target = e.getTarget();
19428         
19429         if(target.nodeName.toLowerCase() === 'i'){
19430             target = Roo.get(target).dom.parentNode;
19431         }
19432         
19433         var nodeName = target.nodeName;
19434         var className = target.className;
19435         var html = target.innerHTML;
19436         //Roo.log(nodeName);
19437         
19438         switch(nodeName.toLowerCase()) {
19439             case 'th':
19440                 switch(className) {
19441                     case 'switch':
19442                         this.showMode(1);
19443                         break;
19444                     case 'prev':
19445                     case 'next':
19446                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
19447                         switch(this.viewMode){
19448                                 case 0:
19449                                         this.viewDate = this.moveMonth(this.viewDate, dir);
19450                                         break;
19451                                 case 1:
19452                                 case 2:
19453                                         this.viewDate = this.moveYear(this.viewDate, dir);
19454                                         break;
19455                         }
19456                         this.fill();
19457                         break;
19458                     case 'today':
19459                         var date = new Date();
19460                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
19461 //                        this.fill()
19462                         this.setValue(this.formatDate(this.date));
19463                         
19464                         this.hidePopup();
19465                         break;
19466                 }
19467                 break;
19468             case 'span':
19469                 if (className.indexOf('disabled') < 0) {
19470                     this.viewDate.setUTCDate(1);
19471                     if (className.indexOf('month') > -1) {
19472                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
19473                     } else {
19474                         var year = parseInt(html, 10) || 0;
19475                         this.viewDate.setUTCFullYear(year);
19476                         
19477                     }
19478                     
19479                     if(this.singleMode){
19480                         this.setValue(this.formatDate(this.viewDate));
19481                         this.hidePopup();
19482                         return;
19483                     }
19484                     
19485                     this.showMode(-1);
19486                     this.fill();
19487                 }
19488                 break;
19489                 
19490             case 'td':
19491                 //Roo.log(className);
19492                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
19493                     var day = parseInt(html, 10) || 1;
19494                     var year = this.viewDate.getUTCFullYear(),
19495                         month = this.viewDate.getUTCMonth();
19496
19497                     if (className.indexOf('old') > -1) {
19498                         if(month === 0 ){
19499                             month = 11;
19500                             year -= 1;
19501                         }else{
19502                             month -= 1;
19503                         }
19504                     } else if (className.indexOf('new') > -1) {
19505                         if (month == 11) {
19506                             month = 0;
19507                             year += 1;
19508                         } else {
19509                             month += 1;
19510                         }
19511                     }
19512                     //Roo.log([year,month,day]);
19513                     this.date = this.UTCDate(year, month, day,0,0,0,0);
19514                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
19515 //                    this.fill();
19516                     //Roo.log(this.formatDate(this.date));
19517                     this.setValue(this.formatDate(this.date));
19518                     this.hidePopup();
19519                 }
19520                 break;
19521         }
19522     },
19523     
19524     setStartDate: function(startDate)
19525     {
19526         this.startDate = startDate || -Infinity;
19527         if (this.startDate !== -Infinity) {
19528             this.startDate = this.parseDate(this.startDate);
19529         }
19530         this.update();
19531         this.updateNavArrows();
19532     },
19533
19534     setEndDate: function(endDate)
19535     {
19536         this.endDate = endDate || Infinity;
19537         if (this.endDate !== Infinity) {
19538             this.endDate = this.parseDate(this.endDate);
19539         }
19540         this.update();
19541         this.updateNavArrows();
19542     },
19543     
19544     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
19545     {
19546         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
19547         if (typeof(this.daysOfWeekDisabled) !== 'object') {
19548             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
19549         }
19550         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
19551             return parseInt(d, 10);
19552         });
19553         this.update();
19554         this.updateNavArrows();
19555     },
19556     
19557     updateNavArrows: function() 
19558     {
19559         if(this.singleMode){
19560             return;
19561         }
19562         
19563         var d = new Date(this.viewDate),
19564         year = d.getUTCFullYear(),
19565         month = d.getUTCMonth();
19566         
19567         Roo.each(this.picker().select('.prev', true).elements, function(v){
19568             v.show();
19569             switch (this.viewMode) {
19570                 case 0:
19571
19572                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
19573                         v.hide();
19574                     }
19575                     break;
19576                 case 1:
19577                 case 2:
19578                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
19579                         v.hide();
19580                     }
19581                     break;
19582             }
19583         });
19584         
19585         Roo.each(this.picker().select('.next', true).elements, function(v){
19586             v.show();
19587             switch (this.viewMode) {
19588                 case 0:
19589
19590                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19591                         v.hide();
19592                     }
19593                     break;
19594                 case 1:
19595                 case 2:
19596                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19597                         v.hide();
19598                     }
19599                     break;
19600             }
19601         })
19602     },
19603     
19604     moveMonth: function(date, dir)
19605     {
19606         if (!dir) {
19607             return date;
19608         }
19609         var new_date = new Date(date.valueOf()),
19610         day = new_date.getUTCDate(),
19611         month = new_date.getUTCMonth(),
19612         mag = Math.abs(dir),
19613         new_month, test;
19614         dir = dir > 0 ? 1 : -1;
19615         if (mag == 1){
19616             test = dir == -1
19617             // If going back one month, make sure month is not current month
19618             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19619             ? function(){
19620                 return new_date.getUTCMonth() == month;
19621             }
19622             // If going forward one month, make sure month is as expected
19623             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19624             : function(){
19625                 return new_date.getUTCMonth() != new_month;
19626             };
19627             new_month = month + dir;
19628             new_date.setUTCMonth(new_month);
19629             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19630             if (new_month < 0 || new_month > 11) {
19631                 new_month = (new_month + 12) % 12;
19632             }
19633         } else {
19634             // For magnitudes >1, move one month at a time...
19635             for (var i=0; i<mag; i++) {
19636                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19637                 new_date = this.moveMonth(new_date, dir);
19638             }
19639             // ...then reset the day, keeping it in the new month
19640             new_month = new_date.getUTCMonth();
19641             new_date.setUTCDate(day);
19642             test = function(){
19643                 return new_month != new_date.getUTCMonth();
19644             };
19645         }
19646         // Common date-resetting loop -- if date is beyond end of month, make it
19647         // end of month
19648         while (test()){
19649             new_date.setUTCDate(--day);
19650             new_date.setUTCMonth(new_month);
19651         }
19652         return new_date;
19653     },
19654
19655     moveYear: function(date, dir)
19656     {
19657         return this.moveMonth(date, dir*12);
19658     },
19659
19660     dateWithinRange: function(date)
19661     {
19662         return date >= this.startDate && date <= this.endDate;
19663     },
19664
19665     
19666     remove: function() 
19667     {
19668         this.picker().remove();
19669     },
19670     
19671     validateValue : function(value)
19672     {
19673         if(this.getVisibilityEl().hasClass('hidden')){
19674             return true;
19675         }
19676         
19677         if(value.length < 1)  {
19678             if(this.allowBlank){
19679                 return true;
19680             }
19681             return false;
19682         }
19683         
19684         if(value.length < this.minLength){
19685             return false;
19686         }
19687         if(value.length > this.maxLength){
19688             return false;
19689         }
19690         if(this.vtype){
19691             var vt = Roo.form.VTypes;
19692             if(!vt[this.vtype](value, this)){
19693                 return false;
19694             }
19695         }
19696         if(typeof this.validator == "function"){
19697             var msg = this.validator(value);
19698             if(msg !== true){
19699                 return false;
19700             }
19701         }
19702         
19703         if(this.regex && !this.regex.test(value)){
19704             return false;
19705         }
19706         
19707         if(typeof(this.parseDate(value)) == 'undefined'){
19708             return false;
19709         }
19710         
19711         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19712             return false;
19713         }      
19714         
19715         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19716             return false;
19717         } 
19718         
19719         
19720         return true;
19721     },
19722     
19723     reset : function()
19724     {
19725         this.date = this.viewDate = '';
19726         
19727         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
19728     }
19729    
19730 });
19731
19732 Roo.apply(Roo.bootstrap.DateField,  {
19733     
19734     head : {
19735         tag: 'thead',
19736         cn: [
19737         {
19738             tag: 'tr',
19739             cn: [
19740             {
19741                 tag: 'th',
19742                 cls: 'prev',
19743                 html: '<i class="fa fa-arrow-left"/>'
19744             },
19745             {
19746                 tag: 'th',
19747                 cls: 'switch',
19748                 colspan: '5'
19749             },
19750             {
19751                 tag: 'th',
19752                 cls: 'next',
19753                 html: '<i class="fa fa-arrow-right"/>'
19754             }
19755
19756             ]
19757         }
19758         ]
19759     },
19760     
19761     content : {
19762         tag: 'tbody',
19763         cn: [
19764         {
19765             tag: 'tr',
19766             cn: [
19767             {
19768                 tag: 'td',
19769                 colspan: '7'
19770             }
19771             ]
19772         }
19773         ]
19774     },
19775     
19776     footer : {
19777         tag: 'tfoot',
19778         cn: [
19779         {
19780             tag: 'tr',
19781             cn: [
19782             {
19783                 tag: 'th',
19784                 colspan: '7',
19785                 cls: 'today'
19786             }
19787                     
19788             ]
19789         }
19790         ]
19791     },
19792     
19793     dates:{
19794         en: {
19795             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19796             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19797             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19798             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19799             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19800             today: "Today"
19801         }
19802     },
19803     
19804     modes: [
19805     {
19806         clsName: 'days',
19807         navFnc: 'Month',
19808         navStep: 1
19809     },
19810     {
19811         clsName: 'months',
19812         navFnc: 'FullYear',
19813         navStep: 1
19814     },
19815     {
19816         clsName: 'years',
19817         navFnc: 'FullYear',
19818         navStep: 10
19819     }]
19820 });
19821
19822 Roo.apply(Roo.bootstrap.DateField,  {
19823   
19824     template : {
19825         tag: 'div',
19826         cls: 'datepicker dropdown-menu roo-dynamic',
19827         cn: [
19828         {
19829             tag: 'div',
19830             cls: 'datepicker-days',
19831             cn: [
19832             {
19833                 tag: 'table',
19834                 cls: 'table-condensed',
19835                 cn:[
19836                 Roo.bootstrap.DateField.head,
19837                 {
19838                     tag: 'tbody'
19839                 },
19840                 Roo.bootstrap.DateField.footer
19841                 ]
19842             }
19843             ]
19844         },
19845         {
19846             tag: 'div',
19847             cls: 'datepicker-months',
19848             cn: [
19849             {
19850                 tag: 'table',
19851                 cls: 'table-condensed',
19852                 cn:[
19853                 Roo.bootstrap.DateField.head,
19854                 Roo.bootstrap.DateField.content,
19855                 Roo.bootstrap.DateField.footer
19856                 ]
19857             }
19858             ]
19859         },
19860         {
19861             tag: 'div',
19862             cls: 'datepicker-years',
19863             cn: [
19864             {
19865                 tag: 'table',
19866                 cls: 'table-condensed',
19867                 cn:[
19868                 Roo.bootstrap.DateField.head,
19869                 Roo.bootstrap.DateField.content,
19870                 Roo.bootstrap.DateField.footer
19871                 ]
19872             }
19873             ]
19874         }
19875         ]
19876     }
19877 });
19878
19879  
19880
19881  /*
19882  * - LGPL
19883  *
19884  * TimeField
19885  * 
19886  */
19887
19888 /**
19889  * @class Roo.bootstrap.TimeField
19890  * @extends Roo.bootstrap.Input
19891  * Bootstrap DateField class
19892  * 
19893  * 
19894  * @constructor
19895  * Create a new TimeField
19896  * @param {Object} config The config object
19897  */
19898
19899 Roo.bootstrap.TimeField = function(config){
19900     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19901     this.addEvents({
19902             /**
19903              * @event show
19904              * Fires when this field show.
19905              * @param {Roo.bootstrap.DateField} thisthis
19906              * @param {Mixed} date The date value
19907              */
19908             show : true,
19909             /**
19910              * @event show
19911              * Fires when this field hide.
19912              * @param {Roo.bootstrap.DateField} this
19913              * @param {Mixed} date The date value
19914              */
19915             hide : true,
19916             /**
19917              * @event select
19918              * Fires when select a date.
19919              * @param {Roo.bootstrap.DateField} this
19920              * @param {Mixed} date The date value
19921              */
19922             select : true
19923         });
19924 };
19925
19926 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19927     
19928     /**
19929      * @cfg {String} format
19930      * The default time format string which can be overriden for localization support.  The format must be
19931      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19932      */
19933     format : "H:i",
19934        
19935     onRender: function(ct, position)
19936     {
19937         
19938         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19939                 
19940         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19941         
19942         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19943         
19944         this.pop = this.picker().select('>.datepicker-time',true).first();
19945         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19946         
19947         this.picker().on('mousedown', this.onMousedown, this);
19948         this.picker().on('click', this.onClick, this);
19949         
19950         this.picker().addClass('datepicker-dropdown');
19951     
19952         this.fillTime();
19953         this.update();
19954             
19955         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19956         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19957         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19958         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19959         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19960         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19961
19962     },
19963     
19964     fireKey: function(e){
19965         if (!this.picker().isVisible()){
19966             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19967                 this.show();
19968             }
19969             return;
19970         }
19971
19972         e.preventDefault();
19973         
19974         switch(e.keyCode){
19975             case 27: // escape
19976                 this.hide();
19977                 break;
19978             case 37: // left
19979             case 39: // right
19980                 this.onTogglePeriod();
19981                 break;
19982             case 38: // up
19983                 this.onIncrementMinutes();
19984                 break;
19985             case 40: // down
19986                 this.onDecrementMinutes();
19987                 break;
19988             case 13: // enter
19989             case 9: // tab
19990                 this.setTime();
19991                 break;
19992         }
19993     },
19994     
19995     onClick: function(e) {
19996         e.stopPropagation();
19997         e.preventDefault();
19998     },
19999     
20000     picker : function()
20001     {
20002         return this.el.select('.datepicker', true).first();
20003     },
20004     
20005     fillTime: function()
20006     {    
20007         var time = this.pop.select('tbody', true).first();
20008         
20009         time.dom.innerHTML = '';
20010         
20011         time.createChild({
20012             tag: 'tr',
20013             cn: [
20014                 {
20015                     tag: 'td',
20016                     cn: [
20017                         {
20018                             tag: 'a',
20019                             href: '#',
20020                             cls: 'btn',
20021                             cn: [
20022                                 {
20023                                     tag: 'span',
20024                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
20025                                 }
20026                             ]
20027                         } 
20028                     ]
20029                 },
20030                 {
20031                     tag: 'td',
20032                     cls: 'separator'
20033                 },
20034                 {
20035                     tag: 'td',
20036                     cn: [
20037                         {
20038                             tag: 'a',
20039                             href: '#',
20040                             cls: 'btn',
20041                             cn: [
20042                                 {
20043                                     tag: 'span',
20044                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
20045                                 }
20046                             ]
20047                         }
20048                     ]
20049                 },
20050                 {
20051                     tag: 'td',
20052                     cls: 'separator'
20053                 }
20054             ]
20055         });
20056         
20057         time.createChild({
20058             tag: 'tr',
20059             cn: [
20060                 {
20061                     tag: 'td',
20062                     cn: [
20063                         {
20064                             tag: 'span',
20065                             cls: 'timepicker-hour',
20066                             html: '00'
20067                         }  
20068                     ]
20069                 },
20070                 {
20071                     tag: 'td',
20072                     cls: 'separator',
20073                     html: ':'
20074                 },
20075                 {
20076                     tag: 'td',
20077                     cn: [
20078                         {
20079                             tag: 'span',
20080                             cls: 'timepicker-minute',
20081                             html: '00'
20082                         }  
20083                     ]
20084                 },
20085                 {
20086                     tag: 'td',
20087                     cls: 'separator'
20088                 },
20089                 {
20090                     tag: 'td',
20091                     cn: [
20092                         {
20093                             tag: 'button',
20094                             type: 'button',
20095                             cls: 'btn btn-primary period',
20096                             html: 'AM'
20097                             
20098                         }
20099                     ]
20100                 }
20101             ]
20102         });
20103         
20104         time.createChild({
20105             tag: 'tr',
20106             cn: [
20107                 {
20108                     tag: 'td',
20109                     cn: [
20110                         {
20111                             tag: 'a',
20112                             href: '#',
20113                             cls: 'btn',
20114                             cn: [
20115                                 {
20116                                     tag: 'span',
20117                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
20118                                 }
20119                             ]
20120                         }
20121                     ]
20122                 },
20123                 {
20124                     tag: 'td',
20125                     cls: 'separator'
20126                 },
20127                 {
20128                     tag: 'td',
20129                     cn: [
20130                         {
20131                             tag: 'a',
20132                             href: '#',
20133                             cls: 'btn',
20134                             cn: [
20135                                 {
20136                                     tag: 'span',
20137                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
20138                                 }
20139                             ]
20140                         }
20141                     ]
20142                 },
20143                 {
20144                     tag: 'td',
20145                     cls: 'separator'
20146                 }
20147             ]
20148         });
20149         
20150     },
20151     
20152     update: function()
20153     {
20154         
20155         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
20156         
20157         this.fill();
20158     },
20159     
20160     fill: function() 
20161     {
20162         var hours = this.time.getHours();
20163         var minutes = this.time.getMinutes();
20164         var period = 'AM';
20165         
20166         if(hours > 11){
20167             period = 'PM';
20168         }
20169         
20170         if(hours == 0){
20171             hours = 12;
20172         }
20173         
20174         
20175         if(hours > 12){
20176             hours = hours - 12;
20177         }
20178         
20179         if(hours < 10){
20180             hours = '0' + hours;
20181         }
20182         
20183         if(minutes < 10){
20184             minutes = '0' + minutes;
20185         }
20186         
20187         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
20188         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
20189         this.pop.select('button', true).first().dom.innerHTML = period;
20190         
20191     },
20192     
20193     place: function()
20194     {   
20195         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
20196         
20197         var cls = ['bottom'];
20198         
20199         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
20200             cls.pop();
20201             cls.push('top');
20202         }
20203         
20204         cls.push('right');
20205         
20206         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
20207             cls.pop();
20208             cls.push('left');
20209         }
20210         
20211         this.picker().addClass(cls.join('-'));
20212         
20213         var _this = this;
20214         
20215         Roo.each(cls, function(c){
20216             if(c == 'bottom'){
20217                 _this.picker().setTop(_this.inputEl().getHeight());
20218                 return;
20219             }
20220             if(c == 'top'){
20221                 _this.picker().setTop(0 - _this.picker().getHeight());
20222                 return;
20223             }
20224             
20225             if(c == 'left'){
20226                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
20227                 return;
20228             }
20229             if(c == 'right'){
20230                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
20231                 return;
20232             }
20233         });
20234         
20235     },
20236   
20237     onFocus : function()
20238     {
20239         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
20240         this.show();
20241     },
20242     
20243     onBlur : function()
20244     {
20245         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
20246         this.hide();
20247     },
20248     
20249     show : function()
20250     {
20251         this.picker().show();
20252         this.pop.show();
20253         this.update();
20254         this.place();
20255         
20256         this.fireEvent('show', this, this.date);
20257     },
20258     
20259     hide : function()
20260     {
20261         this.picker().hide();
20262         this.pop.hide();
20263         
20264         this.fireEvent('hide', this, this.date);
20265     },
20266     
20267     setTime : function()
20268     {
20269         this.hide();
20270         this.setValue(this.time.format(this.format));
20271         
20272         this.fireEvent('select', this, this.date);
20273         
20274         
20275     },
20276     
20277     onMousedown: function(e){
20278         e.stopPropagation();
20279         e.preventDefault();
20280     },
20281     
20282     onIncrementHours: function()
20283     {
20284         Roo.log('onIncrementHours');
20285         this.time = this.time.add(Date.HOUR, 1);
20286         this.update();
20287         
20288     },
20289     
20290     onDecrementHours: function()
20291     {
20292         Roo.log('onDecrementHours');
20293         this.time = this.time.add(Date.HOUR, -1);
20294         this.update();
20295     },
20296     
20297     onIncrementMinutes: function()
20298     {
20299         Roo.log('onIncrementMinutes');
20300         this.time = this.time.add(Date.MINUTE, 1);
20301         this.update();
20302     },
20303     
20304     onDecrementMinutes: function()
20305     {
20306         Roo.log('onDecrementMinutes');
20307         this.time = this.time.add(Date.MINUTE, -1);
20308         this.update();
20309     },
20310     
20311     onTogglePeriod: function()
20312     {
20313         Roo.log('onTogglePeriod');
20314         this.time = this.time.add(Date.HOUR, 12);
20315         this.update();
20316     }
20317     
20318    
20319 });
20320
20321 Roo.apply(Roo.bootstrap.TimeField,  {
20322     
20323     content : {
20324         tag: 'tbody',
20325         cn: [
20326             {
20327                 tag: 'tr',
20328                 cn: [
20329                 {
20330                     tag: 'td',
20331                     colspan: '7'
20332                 }
20333                 ]
20334             }
20335         ]
20336     },
20337     
20338     footer : {
20339         tag: 'tfoot',
20340         cn: [
20341             {
20342                 tag: 'tr',
20343                 cn: [
20344                 {
20345                     tag: 'th',
20346                     colspan: '7',
20347                     cls: '',
20348                     cn: [
20349                         {
20350                             tag: 'button',
20351                             cls: 'btn btn-info ok',
20352                             html: 'OK'
20353                         }
20354                     ]
20355                 }
20356
20357                 ]
20358             }
20359         ]
20360     }
20361 });
20362
20363 Roo.apply(Roo.bootstrap.TimeField,  {
20364   
20365     template : {
20366         tag: 'div',
20367         cls: 'datepicker dropdown-menu',
20368         cn: [
20369             {
20370                 tag: 'div',
20371                 cls: 'datepicker-time',
20372                 cn: [
20373                 {
20374                     tag: 'table',
20375                     cls: 'table-condensed',
20376                     cn:[
20377                     Roo.bootstrap.TimeField.content,
20378                     Roo.bootstrap.TimeField.footer
20379                     ]
20380                 }
20381                 ]
20382             }
20383         ]
20384     }
20385 });
20386
20387  
20388
20389  /*
20390  * - LGPL
20391  *
20392  * MonthField
20393  * 
20394  */
20395
20396 /**
20397  * @class Roo.bootstrap.MonthField
20398  * @extends Roo.bootstrap.Input
20399  * Bootstrap MonthField class
20400  * 
20401  * @cfg {String} language default en
20402  * 
20403  * @constructor
20404  * Create a new MonthField
20405  * @param {Object} config The config object
20406  */
20407
20408 Roo.bootstrap.MonthField = function(config){
20409     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
20410     
20411     this.addEvents({
20412         /**
20413          * @event show
20414          * Fires when this field show.
20415          * @param {Roo.bootstrap.MonthField} this
20416          * @param {Mixed} date The date value
20417          */
20418         show : true,
20419         /**
20420          * @event show
20421          * Fires when this field hide.
20422          * @param {Roo.bootstrap.MonthField} this
20423          * @param {Mixed} date The date value
20424          */
20425         hide : true,
20426         /**
20427          * @event select
20428          * Fires when select a date.
20429          * @param {Roo.bootstrap.MonthField} this
20430          * @param {String} oldvalue The old value
20431          * @param {String} newvalue The new value
20432          */
20433         select : true
20434     });
20435 };
20436
20437 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
20438     
20439     onRender: function(ct, position)
20440     {
20441         
20442         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
20443         
20444         this.language = this.language || 'en';
20445         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
20446         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
20447         
20448         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
20449         this.isInline = false;
20450         this.isInput = true;
20451         this.component = this.el.select('.add-on', true).first() || false;
20452         this.component = (this.component && this.component.length === 0) ? false : this.component;
20453         this.hasInput = this.component && this.inputEL().length;
20454         
20455         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
20456         
20457         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20458         
20459         this.picker().on('mousedown', this.onMousedown, this);
20460         this.picker().on('click', this.onClick, this);
20461         
20462         this.picker().addClass('datepicker-dropdown');
20463         
20464         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20465             v.setStyle('width', '189px');
20466         });
20467         
20468         this.fillMonths();
20469         
20470         this.update();
20471         
20472         if(this.isInline) {
20473             this.show();
20474         }
20475         
20476     },
20477     
20478     setValue: function(v, suppressEvent)
20479     {   
20480         var o = this.getValue();
20481         
20482         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
20483         
20484         this.update();
20485
20486         if(suppressEvent !== true){
20487             this.fireEvent('select', this, o, v);
20488         }
20489         
20490     },
20491     
20492     getValue: function()
20493     {
20494         return this.value;
20495     },
20496     
20497     onClick: function(e) 
20498     {
20499         e.stopPropagation();
20500         e.preventDefault();
20501         
20502         var target = e.getTarget();
20503         
20504         if(target.nodeName.toLowerCase() === 'i'){
20505             target = Roo.get(target).dom.parentNode;
20506         }
20507         
20508         var nodeName = target.nodeName;
20509         var className = target.className;
20510         var html = target.innerHTML;
20511         
20512         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
20513             return;
20514         }
20515         
20516         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
20517         
20518         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20519         
20520         this.hide();
20521                         
20522     },
20523     
20524     picker : function()
20525     {
20526         return this.pickerEl;
20527     },
20528     
20529     fillMonths: function()
20530     {    
20531         var i = 0;
20532         var months = this.picker().select('>.datepicker-months td', true).first();
20533         
20534         months.dom.innerHTML = '';
20535         
20536         while (i < 12) {
20537             var month = {
20538                 tag: 'span',
20539                 cls: 'month',
20540                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
20541             };
20542             
20543             months.createChild(month);
20544         }
20545         
20546     },
20547     
20548     update: function()
20549     {
20550         var _this = this;
20551         
20552         if(typeof(this.vIndex) == 'undefined' && this.value.length){
20553             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
20554         }
20555         
20556         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
20557             e.removeClass('active');
20558             
20559             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
20560                 e.addClass('active');
20561             }
20562         })
20563     },
20564     
20565     place: function()
20566     {
20567         if(this.isInline) {
20568             return;
20569         }
20570         
20571         this.picker().removeClass(['bottom', 'top']);
20572         
20573         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20574             /*
20575              * place to the top of element!
20576              *
20577              */
20578             
20579             this.picker().addClass('top');
20580             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20581             
20582             return;
20583         }
20584         
20585         this.picker().addClass('bottom');
20586         
20587         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20588     },
20589     
20590     onFocus : function()
20591     {
20592         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
20593         this.show();
20594     },
20595     
20596     onBlur : function()
20597     {
20598         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
20599         
20600         var d = this.inputEl().getValue();
20601         
20602         this.setValue(d);
20603                 
20604         this.hide();
20605     },
20606     
20607     show : function()
20608     {
20609         this.picker().show();
20610         this.picker().select('>.datepicker-months', true).first().show();
20611         this.update();
20612         this.place();
20613         
20614         this.fireEvent('show', this, this.date);
20615     },
20616     
20617     hide : function()
20618     {
20619         if(this.isInline) {
20620             return;
20621         }
20622         this.picker().hide();
20623         this.fireEvent('hide', this, this.date);
20624         
20625     },
20626     
20627     onMousedown: function(e)
20628     {
20629         e.stopPropagation();
20630         e.preventDefault();
20631     },
20632     
20633     keyup: function(e)
20634     {
20635         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20636         this.update();
20637     },
20638
20639     fireKey: function(e)
20640     {
20641         if (!this.picker().isVisible()){
20642             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20643                 this.show();
20644             }
20645             return;
20646         }
20647         
20648         var dir;
20649         
20650         switch(e.keyCode){
20651             case 27: // escape
20652                 this.hide();
20653                 e.preventDefault();
20654                 break;
20655             case 37: // left
20656             case 39: // right
20657                 dir = e.keyCode == 37 ? -1 : 1;
20658                 
20659                 this.vIndex = this.vIndex + dir;
20660                 
20661                 if(this.vIndex < 0){
20662                     this.vIndex = 0;
20663                 }
20664                 
20665                 if(this.vIndex > 11){
20666                     this.vIndex = 11;
20667                 }
20668                 
20669                 if(isNaN(this.vIndex)){
20670                     this.vIndex = 0;
20671                 }
20672                 
20673                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20674                 
20675                 break;
20676             case 38: // up
20677             case 40: // down
20678                 
20679                 dir = e.keyCode == 38 ? -1 : 1;
20680                 
20681                 this.vIndex = this.vIndex + dir * 4;
20682                 
20683                 if(this.vIndex < 0){
20684                     this.vIndex = 0;
20685                 }
20686                 
20687                 if(this.vIndex > 11){
20688                     this.vIndex = 11;
20689                 }
20690                 
20691                 if(isNaN(this.vIndex)){
20692                     this.vIndex = 0;
20693                 }
20694                 
20695                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20696                 break;
20697                 
20698             case 13: // enter
20699                 
20700                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20701                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20702                 }
20703                 
20704                 this.hide();
20705                 e.preventDefault();
20706                 break;
20707             case 9: // tab
20708                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20709                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20710                 }
20711                 this.hide();
20712                 break;
20713             case 16: // shift
20714             case 17: // ctrl
20715             case 18: // alt
20716                 break;
20717             default :
20718                 this.hide();
20719                 
20720         }
20721     },
20722     
20723     remove: function() 
20724     {
20725         this.picker().remove();
20726     }
20727    
20728 });
20729
20730 Roo.apply(Roo.bootstrap.MonthField,  {
20731     
20732     content : {
20733         tag: 'tbody',
20734         cn: [
20735         {
20736             tag: 'tr',
20737             cn: [
20738             {
20739                 tag: 'td',
20740                 colspan: '7'
20741             }
20742             ]
20743         }
20744         ]
20745     },
20746     
20747     dates:{
20748         en: {
20749             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20750             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20751         }
20752     }
20753 });
20754
20755 Roo.apply(Roo.bootstrap.MonthField,  {
20756   
20757     template : {
20758         tag: 'div',
20759         cls: 'datepicker dropdown-menu roo-dynamic',
20760         cn: [
20761             {
20762                 tag: 'div',
20763                 cls: 'datepicker-months',
20764                 cn: [
20765                 {
20766                     tag: 'table',
20767                     cls: 'table-condensed',
20768                     cn:[
20769                         Roo.bootstrap.DateField.content
20770                     ]
20771                 }
20772                 ]
20773             }
20774         ]
20775     }
20776 });
20777
20778  
20779
20780  
20781  /*
20782  * - LGPL
20783  *
20784  * CheckBox
20785  * 
20786  */
20787
20788 /**
20789  * @class Roo.bootstrap.CheckBox
20790  * @extends Roo.bootstrap.Input
20791  * Bootstrap CheckBox class
20792  * 
20793  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20794  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20795  * @cfg {String} boxLabel The text that appears beside the checkbox
20796  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20797  * @cfg {Boolean} checked initnal the element
20798  * @cfg {Boolean} inline inline the element (default false)
20799  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20800  * @cfg {String} tooltip label tooltip
20801  * 
20802  * @constructor
20803  * Create a new CheckBox
20804  * @param {Object} config The config object
20805  */
20806
20807 Roo.bootstrap.CheckBox = function(config){
20808     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20809    
20810     this.addEvents({
20811         /**
20812         * @event check
20813         * Fires when the element is checked or unchecked.
20814         * @param {Roo.bootstrap.CheckBox} this This input
20815         * @param {Boolean} checked The new checked value
20816         */
20817        check : true,
20818        /**
20819         * @event click
20820         * Fires when the element is click.
20821         * @param {Roo.bootstrap.CheckBox} this This input
20822         */
20823        click : true
20824     });
20825     
20826 };
20827
20828 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20829   
20830     inputType: 'checkbox',
20831     inputValue: 1,
20832     valueOff: 0,
20833     boxLabel: false,
20834     checked: false,
20835     weight : false,
20836     inline: false,
20837     tooltip : '',
20838     
20839     getAutoCreate : function()
20840     {
20841         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20842         
20843         var id = Roo.id();
20844         
20845         var cfg = {};
20846         
20847         cfg.cls = 'form-group ' + this.inputType; //input-group
20848         
20849         if(this.inline){
20850             cfg.cls += ' ' + this.inputType + '-inline';
20851         }
20852         
20853         var input =  {
20854             tag: 'input',
20855             id : id,
20856             type : this.inputType,
20857             value : this.inputValue,
20858             cls : 'roo-' + this.inputType, //'form-box',
20859             placeholder : this.placeholder || ''
20860             
20861         };
20862         
20863         if(this.inputType != 'radio'){
20864             var hidden =  {
20865                 tag: 'input',
20866                 type : 'hidden',
20867                 cls : 'roo-hidden-value',
20868                 value : this.checked ? this.inputValue : this.valueOff
20869             };
20870         }
20871         
20872             
20873         if (this.weight) { // Validity check?
20874             cfg.cls += " " + this.inputType + "-" + this.weight;
20875         }
20876         
20877         if (this.disabled) {
20878             input.disabled=true;
20879         }
20880         
20881         if(this.checked){
20882             input.checked = this.checked;
20883         }
20884         
20885         if (this.name) {
20886             
20887             input.name = this.name;
20888             
20889             if(this.inputType != 'radio'){
20890                 hidden.name = this.name;
20891                 input.name = '_hidden_' + this.name;
20892             }
20893         }
20894         
20895         if (this.size) {
20896             input.cls += ' input-' + this.size;
20897         }
20898         
20899         var settings=this;
20900         
20901         ['xs','sm','md','lg'].map(function(size){
20902             if (settings[size]) {
20903                 cfg.cls += ' col-' + size + '-' + settings[size];
20904             }
20905         });
20906         
20907         var inputblock = input;
20908          
20909         if (this.before || this.after) {
20910             
20911             inputblock = {
20912                 cls : 'input-group',
20913                 cn :  [] 
20914             };
20915             
20916             if (this.before) {
20917                 inputblock.cn.push({
20918                     tag :'span',
20919                     cls : 'input-group-addon',
20920                     html : this.before
20921                 });
20922             }
20923             
20924             inputblock.cn.push(input);
20925             
20926             if(this.inputType != 'radio'){
20927                 inputblock.cn.push(hidden);
20928             }
20929             
20930             if (this.after) {
20931                 inputblock.cn.push({
20932                     tag :'span',
20933                     cls : 'input-group-addon',
20934                     html : this.after
20935                 });
20936             }
20937             
20938         }
20939         
20940         if (align ==='left' && this.fieldLabel.length) {
20941 //                Roo.log("left and has label");
20942             cfg.cn = [
20943                 {
20944                     tag: 'label',
20945                     'for' :  id,
20946                     cls : 'control-label',
20947                     html : this.fieldLabel
20948                 },
20949                 {
20950                     cls : "", 
20951                     cn: [
20952                         inputblock
20953                     ]
20954                 }
20955             ];
20956             
20957             if(this.labelWidth > 12){
20958                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20959             }
20960             
20961             if(this.labelWidth < 13 && this.labelmd == 0){
20962                 this.labelmd = this.labelWidth;
20963             }
20964             
20965             if(this.labellg > 0){
20966                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20967                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20968             }
20969             
20970             if(this.labelmd > 0){
20971                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20972                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20973             }
20974             
20975             if(this.labelsm > 0){
20976                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20977                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20978             }
20979             
20980             if(this.labelxs > 0){
20981                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20982                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20983             }
20984             
20985         } else if ( this.fieldLabel.length) {
20986 //                Roo.log(" label");
20987                 cfg.cn = [
20988                    
20989                     {
20990                         tag: this.boxLabel ? 'span' : 'label',
20991                         'for': id,
20992                         cls: 'control-label box-input-label',
20993                         //cls : 'input-group-addon',
20994                         html : this.fieldLabel
20995                     },
20996                     
20997                     inputblock
20998                     
20999                 ];
21000
21001         } else {
21002             
21003 //                Roo.log(" no label && no align");
21004                 cfg.cn = [  inputblock ] ;
21005                 
21006                 
21007         }
21008         
21009         if(this.boxLabel){
21010              var boxLabelCfg = {
21011                 tag: 'label',
21012                 //'for': id, // box label is handled by onclick - so no for...
21013                 cls: 'box-label',
21014                 html: this.boxLabel
21015             };
21016             
21017             if(this.tooltip){
21018                 boxLabelCfg.tooltip = this.tooltip;
21019             }
21020              
21021             cfg.cn.push(boxLabelCfg);
21022         }
21023         
21024         if(this.inputType != 'radio'){
21025             cfg.cn.push(hidden);
21026         }
21027         
21028         return cfg;
21029         
21030     },
21031     
21032     /**
21033      * return the real input element.
21034      */
21035     inputEl: function ()
21036     {
21037         return this.el.select('input.roo-' + this.inputType,true).first();
21038     },
21039     hiddenEl: function ()
21040     {
21041         return this.el.select('input.roo-hidden-value',true).first();
21042     },
21043     
21044     labelEl: function()
21045     {
21046         return this.el.select('label.control-label',true).first();
21047     },
21048     /* depricated... */
21049     
21050     label: function()
21051     {
21052         return this.labelEl();
21053     },
21054     
21055     boxLabelEl: function()
21056     {
21057         return this.el.select('label.box-label',true).first();
21058     },
21059     
21060     initEvents : function()
21061     {
21062 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
21063         
21064         this.inputEl().on('click', this.onClick,  this);
21065         
21066         if (this.boxLabel) { 
21067             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
21068         }
21069         
21070         this.startValue = this.getValue();
21071         
21072         if(this.groupId){
21073             Roo.bootstrap.CheckBox.register(this);
21074         }
21075     },
21076     
21077     onClick : function(e)
21078     {   
21079         if(this.fireEvent('click', this, e) !== false){
21080             this.setChecked(!this.checked);
21081         }
21082         
21083     },
21084     
21085     setChecked : function(state,suppressEvent)
21086     {
21087         this.startValue = this.getValue();
21088
21089         if(this.inputType == 'radio'){
21090             
21091             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21092                 e.dom.checked = false;
21093             });
21094             
21095             this.inputEl().dom.checked = true;
21096             
21097             this.inputEl().dom.value = this.inputValue;
21098             
21099             if(suppressEvent !== true){
21100                 this.fireEvent('check', this, true);
21101             }
21102             
21103             this.validate();
21104             
21105             return;
21106         }
21107         
21108         this.checked = state;
21109         
21110         this.inputEl().dom.checked = state;
21111         
21112         
21113         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
21114         
21115         if(suppressEvent !== true){
21116             this.fireEvent('check', this, state);
21117         }
21118         
21119         this.validate();
21120     },
21121     
21122     getValue : function()
21123     {
21124         if(this.inputType == 'radio'){
21125             return this.getGroupValue();
21126         }
21127         
21128         return this.hiddenEl().dom.value;
21129         
21130     },
21131     
21132     getGroupValue : function()
21133     {
21134         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
21135             return '';
21136         }
21137         
21138         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
21139     },
21140     
21141     setValue : function(v,suppressEvent)
21142     {
21143         if(this.inputType == 'radio'){
21144             this.setGroupValue(v, suppressEvent);
21145             return;
21146         }
21147         
21148         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
21149         
21150         this.validate();
21151     },
21152     
21153     setGroupValue : function(v, suppressEvent)
21154     {
21155         this.startValue = this.getValue();
21156         
21157         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21158             e.dom.checked = false;
21159             
21160             if(e.dom.value == v){
21161                 e.dom.checked = true;
21162             }
21163         });
21164         
21165         if(suppressEvent !== true){
21166             this.fireEvent('check', this, true);
21167         }
21168
21169         this.validate();
21170         
21171         return;
21172     },
21173     
21174     validate : function()
21175     {
21176         if(this.getVisibilityEl().hasClass('hidden')){
21177             return true;
21178         }
21179         
21180         if(
21181                 this.disabled || 
21182                 (this.inputType == 'radio' && this.validateRadio()) ||
21183                 (this.inputType == 'checkbox' && this.validateCheckbox())
21184         ){
21185             this.markValid();
21186             return true;
21187         }
21188         
21189         this.markInvalid();
21190         return false;
21191     },
21192     
21193     validateRadio : function()
21194     {
21195         if(this.getVisibilityEl().hasClass('hidden')){
21196             return true;
21197         }
21198         
21199         if(this.allowBlank){
21200             return true;
21201         }
21202         
21203         var valid = false;
21204         
21205         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21206             if(!e.dom.checked){
21207                 return;
21208             }
21209             
21210             valid = true;
21211             
21212             return false;
21213         });
21214         
21215         return valid;
21216     },
21217     
21218     validateCheckbox : function()
21219     {
21220         if(!this.groupId){
21221             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
21222             //return (this.getValue() == this.inputValue) ? true : false;
21223         }
21224         
21225         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21226         
21227         if(!group){
21228             return false;
21229         }
21230         
21231         var r = false;
21232         
21233         for(var i in group){
21234             if(group[i].el.isVisible(true)){
21235                 r = false;
21236                 break;
21237             }
21238             
21239             r = true;
21240         }
21241         
21242         for(var i in group){
21243             if(r){
21244                 break;
21245             }
21246             
21247             r = (group[i].getValue() == group[i].inputValue) ? true : false;
21248         }
21249         
21250         return r;
21251     },
21252     
21253     /**
21254      * Mark this field as valid
21255      */
21256     markValid : function()
21257     {
21258         var _this = this;
21259         
21260         this.fireEvent('valid', this);
21261         
21262         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21263         
21264         if(this.groupId){
21265             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21266         }
21267         
21268         if(label){
21269             label.markValid();
21270         }
21271
21272         if(this.inputType == 'radio'){
21273             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21274                 var fg = e.findParent('.form-group', false, true);
21275                 if (Roo.bootstrap.version == 3) {
21276                     fg.removeClass([_this.invalidClass, _this.validClass]);
21277                     fg.addClass(_this.validClass);
21278                 } else {
21279                     fg.removeClass(['is-valid', 'is-invalid']);
21280                     fg.addClass('is-valid');
21281                 }
21282             });
21283             
21284             return;
21285         }
21286
21287         if(!this.groupId){
21288             var fg = this.el.findParent('.form-group', false, true);
21289             if (Roo.bootstrap.version == 3) {
21290                 fg.removeClass([this.invalidClass, this.validClass]);
21291                 fg.addClass(this.validClass);
21292             } else {
21293                 fg.removeClass(['is-valid', 'is-invalid']);
21294                 fg.addClass('is-valid');
21295             }
21296             return;
21297         }
21298         
21299         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21300         
21301         if(!group){
21302             return;
21303         }
21304         
21305         for(var i in group){
21306             var fg = group[i].el.findParent('.form-group', false, true);
21307             if (Roo.bootstrap.version == 3) {
21308                 fg.removeClass([this.invalidClass, this.validClass]);
21309                 fg.addClass(this.validClass);
21310             } else {
21311                 fg.removeClass(['is-valid', 'is-invalid']);
21312                 fg.addClass('is-valid');
21313             }
21314         }
21315     },
21316     
21317      /**
21318      * Mark this field as invalid
21319      * @param {String} msg The validation message
21320      */
21321     markInvalid : function(msg)
21322     {
21323         if(this.allowBlank){
21324             return;
21325         }
21326         
21327         var _this = this;
21328         
21329         this.fireEvent('invalid', this, msg);
21330         
21331         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21332         
21333         if(this.groupId){
21334             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
21335         }
21336         
21337         if(label){
21338             label.markInvalid();
21339         }
21340             
21341         if(this.inputType == 'radio'){
21342             
21343             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21344                 var fg = e.findParent('.form-group', false, true);
21345                 if (Roo.bootstrap.version == 3) {
21346                     fg.removeClass([_this.invalidClass, _this.validClass]);
21347                     fg.addClass(_this.invalidClass);
21348                 } else {
21349                     fg.removeClass(['is-invalid', 'is-valid']);
21350                     fg.addClass('is-invalid');
21351                 }
21352             });
21353             
21354             return;
21355         }
21356         
21357         if(!this.groupId){
21358             var fg = this.el.findParent('.form-group', false, true);
21359             if (Roo.bootstrap.version == 3) {
21360                 fg.removeClass([_this.invalidClass, _this.validClass]);
21361                 fg.addClass(_this.invalidClass);
21362             } else {
21363                 fg.removeClass(['is-invalid', 'is-valid']);
21364                 fg.addClass('is-invalid');
21365             }
21366             return;
21367         }
21368         
21369         var group = Roo.bootstrap.CheckBox.get(this.groupId);
21370         
21371         if(!group){
21372             return;
21373         }
21374         
21375         for(var i in group){
21376             var fg = group[i].el.findParent('.form-group', false, true);
21377             if (Roo.bootstrap.version == 3) {
21378                 fg.removeClass([_this.invalidClass, _this.validClass]);
21379                 fg.addClass(_this.invalidClass);
21380             } else {
21381                 fg.removeClass(['is-invalid', 'is-valid']);
21382                 fg.addClass('is-invalid');
21383             }
21384         }
21385         
21386     },
21387     
21388     clearInvalid : function()
21389     {
21390         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
21391         
21392         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
21393         
21394         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
21395         
21396         if (label && label.iconEl) {
21397             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
21398             label.iconEl.removeClass(['is-invalid', 'is-valid']);
21399         }
21400     },
21401     
21402     disable : function()
21403     {
21404         if(this.inputType != 'radio'){
21405             Roo.bootstrap.CheckBox.superclass.disable.call(this);
21406             return;
21407         }
21408         
21409         var _this = this;
21410         
21411         if(this.rendered){
21412             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21413                 _this.getActionEl().addClass(this.disabledClass);
21414                 e.dom.disabled = true;
21415             });
21416         }
21417         
21418         this.disabled = true;
21419         this.fireEvent("disable", this);
21420         return this;
21421     },
21422
21423     enable : function()
21424     {
21425         if(this.inputType != 'radio'){
21426             Roo.bootstrap.CheckBox.superclass.enable.call(this);
21427             return;
21428         }
21429         
21430         var _this = this;
21431         
21432         if(this.rendered){
21433             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
21434                 _this.getActionEl().removeClass(this.disabledClass);
21435                 e.dom.disabled = false;
21436             });
21437         }
21438         
21439         this.disabled = false;
21440         this.fireEvent("enable", this);
21441         return this;
21442     },
21443     
21444     setBoxLabel : function(v)
21445     {
21446         this.boxLabel = v;
21447         
21448         if(this.rendered){
21449             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21450         }
21451     }
21452
21453 });
21454
21455 Roo.apply(Roo.bootstrap.CheckBox, {
21456     
21457     groups: {},
21458     
21459      /**
21460     * register a CheckBox Group
21461     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
21462     */
21463     register : function(checkbox)
21464     {
21465         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
21466             this.groups[checkbox.groupId] = {};
21467         }
21468         
21469         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
21470             return;
21471         }
21472         
21473         this.groups[checkbox.groupId][checkbox.name] = checkbox;
21474         
21475     },
21476     /**
21477     * fetch a CheckBox Group based on the group ID
21478     * @param {string} the group ID
21479     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
21480     */
21481     get: function(groupId) {
21482         if (typeof(this.groups[groupId]) == 'undefined') {
21483             return false;
21484         }
21485         
21486         return this.groups[groupId] ;
21487     }
21488     
21489     
21490 });
21491 /*
21492  * - LGPL
21493  *
21494  * RadioItem
21495  * 
21496  */
21497
21498 /**
21499  * @class Roo.bootstrap.Radio
21500  * @extends Roo.bootstrap.Component
21501  * Bootstrap Radio class
21502  * @cfg {String} boxLabel - the label associated
21503  * @cfg {String} value - the value of radio
21504  * 
21505  * @constructor
21506  * Create a new Radio
21507  * @param {Object} config The config object
21508  */
21509 Roo.bootstrap.Radio = function(config){
21510     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
21511     
21512 };
21513
21514 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
21515     
21516     boxLabel : '',
21517     
21518     value : '',
21519     
21520     getAutoCreate : function()
21521     {
21522         var cfg = {
21523             tag : 'div',
21524             cls : 'form-group radio',
21525             cn : [
21526                 {
21527                     tag : 'label',
21528                     cls : 'box-label',
21529                     html : this.boxLabel
21530                 }
21531             ]
21532         };
21533         
21534         return cfg;
21535     },
21536     
21537     initEvents : function() 
21538     {
21539         this.parent().register(this);
21540         
21541         this.el.on('click', this.onClick, this);
21542         
21543     },
21544     
21545     onClick : function(e)
21546     {
21547         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
21548             this.setChecked(true);
21549         }
21550     },
21551     
21552     setChecked : function(state, suppressEvent)
21553     {
21554         this.parent().setValue(this.value, suppressEvent);
21555         
21556     },
21557     
21558     setBoxLabel : function(v)
21559     {
21560         this.boxLabel = v;
21561         
21562         if(this.rendered){
21563             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
21564         }
21565     }
21566     
21567 });
21568  
21569
21570  /*
21571  * - LGPL
21572  *
21573  * Input
21574  * 
21575  */
21576
21577 /**
21578  * @class Roo.bootstrap.SecurePass
21579  * @extends Roo.bootstrap.Input
21580  * Bootstrap SecurePass class
21581  *
21582  * 
21583  * @constructor
21584  * Create a new SecurePass
21585  * @param {Object} config The config object
21586  */
21587  
21588 Roo.bootstrap.SecurePass = function (config) {
21589     // these go here, so the translation tool can replace them..
21590     this.errors = {
21591         PwdEmpty: "Please type a password, and then retype it to confirm.",
21592         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21593         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21594         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21595         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21596         FNInPwd: "Your password can't contain your first name. Please type a different password.",
21597         LNInPwd: "Your password can't contain your last name. Please type a different password.",
21598         TooWeak: "Your password is Too Weak."
21599     },
21600     this.meterLabel = "Password strength:";
21601     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
21602     this.meterClass = [
21603         "roo-password-meter-tooweak", 
21604         "roo-password-meter-weak", 
21605         "roo-password-meter-medium", 
21606         "roo-password-meter-strong", 
21607         "roo-password-meter-grey"
21608     ];
21609     
21610     this.errors = {};
21611     
21612     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
21613 }
21614
21615 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
21616     /**
21617      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
21618      * {
21619      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
21620      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
21621      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
21622      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
21623      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
21624      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
21625      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
21626      * })
21627      */
21628     // private
21629     
21630     meterWidth: 300,
21631     errorMsg :'',    
21632     errors: false,
21633     imageRoot: '/',
21634     /**
21635      * @cfg {String/Object} Label for the strength meter (defaults to
21636      * 'Password strength:')
21637      */
21638     // private
21639     meterLabel: '',
21640     /**
21641      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
21642      * ['Weak', 'Medium', 'Strong'])
21643      */
21644     // private    
21645     pwdStrengths: false,    
21646     // private
21647     strength: 0,
21648     // private
21649     _lastPwd: null,
21650     // private
21651     kCapitalLetter: 0,
21652     kSmallLetter: 1,
21653     kDigit: 2,
21654     kPunctuation: 3,
21655     
21656     insecure: false,
21657     // private
21658     initEvents: function ()
21659     {
21660         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21661
21662         if (this.el.is('input[type=password]') && Roo.isSafari) {
21663             this.el.on('keydown', this.SafariOnKeyDown, this);
21664         }
21665
21666         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21667     },
21668     // private
21669     onRender: function (ct, position)
21670     {
21671         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21672         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21673         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21674
21675         this.trigger.createChild({
21676                    cn: [
21677                     {
21678                     //id: 'PwdMeter',
21679                     tag: 'div',
21680                     cls: 'roo-password-meter-grey col-xs-12',
21681                     style: {
21682                         //width: 0,
21683                         //width: this.meterWidth + 'px'                                                
21684                         }
21685                     },
21686                     {                            
21687                          cls: 'roo-password-meter-text'                          
21688                     }
21689                 ]            
21690         });
21691
21692          
21693         if (this.hideTrigger) {
21694             this.trigger.setDisplayed(false);
21695         }
21696         this.setSize(this.width || '', this.height || '');
21697     },
21698     // private
21699     onDestroy: function ()
21700     {
21701         if (this.trigger) {
21702             this.trigger.removeAllListeners();
21703             this.trigger.remove();
21704         }
21705         if (this.wrap) {
21706             this.wrap.remove();
21707         }
21708         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21709     },
21710     // private
21711     checkStrength: function ()
21712     {
21713         var pwd = this.inputEl().getValue();
21714         if (pwd == this._lastPwd) {
21715             return;
21716         }
21717
21718         var strength;
21719         if (this.ClientSideStrongPassword(pwd)) {
21720             strength = 3;
21721         } else if (this.ClientSideMediumPassword(pwd)) {
21722             strength = 2;
21723         } else if (this.ClientSideWeakPassword(pwd)) {
21724             strength = 1;
21725         } else {
21726             strength = 0;
21727         }
21728         
21729         Roo.log('strength1: ' + strength);
21730         
21731         //var pm = this.trigger.child('div/div/div').dom;
21732         var pm = this.trigger.child('div/div');
21733         pm.removeClass(this.meterClass);
21734         pm.addClass(this.meterClass[strength]);
21735                 
21736         
21737         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21738                 
21739         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21740         
21741         this._lastPwd = pwd;
21742     },
21743     reset: function ()
21744     {
21745         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21746         
21747         this._lastPwd = '';
21748         
21749         var pm = this.trigger.child('div/div');
21750         pm.removeClass(this.meterClass);
21751         pm.addClass('roo-password-meter-grey');        
21752         
21753         
21754         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21755         
21756         pt.innerHTML = '';
21757         this.inputEl().dom.type='password';
21758     },
21759     // private
21760     validateValue: function (value)
21761     {
21762         
21763         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21764             return false;
21765         }
21766         if (value.length == 0) {
21767             if (this.allowBlank) {
21768                 this.clearInvalid();
21769                 return true;
21770             }
21771
21772             this.markInvalid(this.errors.PwdEmpty);
21773             this.errorMsg = this.errors.PwdEmpty;
21774             return false;
21775         }
21776         
21777         if(this.insecure){
21778             return true;
21779         }
21780         
21781         if ('[\x21-\x7e]*'.match(value)) {
21782             this.markInvalid(this.errors.PwdBadChar);
21783             this.errorMsg = this.errors.PwdBadChar;
21784             return false;
21785         }
21786         if (value.length < 6) {
21787             this.markInvalid(this.errors.PwdShort);
21788             this.errorMsg = this.errors.PwdShort;
21789             return false;
21790         }
21791         if (value.length > 16) {
21792             this.markInvalid(this.errors.PwdLong);
21793             this.errorMsg = this.errors.PwdLong;
21794             return false;
21795         }
21796         var strength;
21797         if (this.ClientSideStrongPassword(value)) {
21798             strength = 3;
21799         } else if (this.ClientSideMediumPassword(value)) {
21800             strength = 2;
21801         } else if (this.ClientSideWeakPassword(value)) {
21802             strength = 1;
21803         } else {
21804             strength = 0;
21805         }
21806
21807         
21808         if (strength < 2) {
21809             //this.markInvalid(this.errors.TooWeak);
21810             this.errorMsg = this.errors.TooWeak;
21811             //return false;
21812         }
21813         
21814         
21815         console.log('strength2: ' + strength);
21816         
21817         //var pm = this.trigger.child('div/div/div').dom;
21818         
21819         var pm = this.trigger.child('div/div');
21820         pm.removeClass(this.meterClass);
21821         pm.addClass(this.meterClass[strength]);
21822                 
21823         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21824                 
21825         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21826         
21827         this.errorMsg = ''; 
21828         return true;
21829     },
21830     // private
21831     CharacterSetChecks: function (type)
21832     {
21833         this.type = type;
21834         this.fResult = false;
21835     },
21836     // private
21837     isctype: function (character, type)
21838     {
21839         switch (type) {  
21840             case this.kCapitalLetter:
21841                 if (character >= 'A' && character <= 'Z') {
21842                     return true;
21843                 }
21844                 break;
21845             
21846             case this.kSmallLetter:
21847                 if (character >= 'a' && character <= 'z') {
21848                     return true;
21849                 }
21850                 break;
21851             
21852             case this.kDigit:
21853                 if (character >= '0' && character <= '9') {
21854                     return true;
21855                 }
21856                 break;
21857             
21858             case this.kPunctuation:
21859                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21860                     return true;
21861                 }
21862                 break;
21863             
21864             default:
21865                 return false;
21866         }
21867
21868     },
21869     // private
21870     IsLongEnough: function (pwd, size)
21871     {
21872         return !(pwd == null || isNaN(size) || pwd.length < size);
21873     },
21874     // private
21875     SpansEnoughCharacterSets: function (word, nb)
21876     {
21877         if (!this.IsLongEnough(word, nb))
21878         {
21879             return false;
21880         }
21881
21882         var characterSetChecks = new Array(
21883             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21884             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21885         );
21886         
21887         for (var index = 0; index < word.length; ++index) {
21888             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21889                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21890                     characterSetChecks[nCharSet].fResult = true;
21891                     break;
21892                 }
21893             }
21894         }
21895
21896         var nCharSets = 0;
21897         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21898             if (characterSetChecks[nCharSet].fResult) {
21899                 ++nCharSets;
21900             }
21901         }
21902
21903         if (nCharSets < nb) {
21904             return false;
21905         }
21906         return true;
21907     },
21908     // private
21909     ClientSideStrongPassword: function (pwd)
21910     {
21911         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21912     },
21913     // private
21914     ClientSideMediumPassword: function (pwd)
21915     {
21916         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21917     },
21918     // private
21919     ClientSideWeakPassword: function (pwd)
21920     {
21921         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21922     }
21923           
21924 })//<script type="text/javascript">
21925
21926 /*
21927  * Based  Ext JS Library 1.1.1
21928  * Copyright(c) 2006-2007, Ext JS, LLC.
21929  * LGPL
21930  *
21931  */
21932  
21933 /**
21934  * @class Roo.HtmlEditorCore
21935  * @extends Roo.Component
21936  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21937  *
21938  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21939  */
21940
21941 Roo.HtmlEditorCore = function(config){
21942     
21943     
21944     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21945     
21946     
21947     this.addEvents({
21948         /**
21949          * @event initialize
21950          * Fires when the editor is fully initialized (including the iframe)
21951          * @param {Roo.HtmlEditorCore} this
21952          */
21953         initialize: true,
21954         /**
21955          * @event activate
21956          * Fires when the editor is first receives the focus. Any insertion must wait
21957          * until after this event.
21958          * @param {Roo.HtmlEditorCore} this
21959          */
21960         activate: true,
21961          /**
21962          * @event beforesync
21963          * Fires before the textarea is updated with content from the editor iframe. Return false
21964          * to cancel the sync.
21965          * @param {Roo.HtmlEditorCore} this
21966          * @param {String} html
21967          */
21968         beforesync: true,
21969          /**
21970          * @event beforepush
21971          * Fires before the iframe editor is updated with content from the textarea. Return false
21972          * to cancel the push.
21973          * @param {Roo.HtmlEditorCore} this
21974          * @param {String} html
21975          */
21976         beforepush: true,
21977          /**
21978          * @event sync
21979          * Fires when the textarea is updated with content from the editor iframe.
21980          * @param {Roo.HtmlEditorCore} this
21981          * @param {String} html
21982          */
21983         sync: true,
21984          /**
21985          * @event push
21986          * Fires when the iframe editor is updated with content from the textarea.
21987          * @param {Roo.HtmlEditorCore} this
21988          * @param {String} html
21989          */
21990         push: true,
21991         
21992         /**
21993          * @event editorevent
21994          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21995          * @param {Roo.HtmlEditorCore} this
21996          */
21997         editorevent: true
21998         
21999     });
22000     
22001     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
22002     
22003     // defaults : white / black...
22004     this.applyBlacklists();
22005     
22006     
22007     
22008 };
22009
22010
22011 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
22012
22013
22014      /**
22015      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
22016      */
22017     
22018     owner : false,
22019     
22020      /**
22021      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22022      *                        Roo.resizable.
22023      */
22024     resizable : false,
22025      /**
22026      * @cfg {Number} height (in pixels)
22027      */   
22028     height: 300,
22029    /**
22030      * @cfg {Number} width (in pixels)
22031      */   
22032     width: 500,
22033     
22034     /**
22035      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22036      * 
22037      */
22038     stylesheets: false,
22039     
22040     // id of frame..
22041     frameId: false,
22042     
22043     // private properties
22044     validationEvent : false,
22045     deferHeight: true,
22046     initialized : false,
22047     activated : false,
22048     sourceEditMode : false,
22049     onFocus : Roo.emptyFn,
22050     iframePad:3,
22051     hideMode:'offsets',
22052     
22053     clearUp: true,
22054     
22055     // blacklist + whitelisted elements..
22056     black: false,
22057     white: false,
22058      
22059     bodyCls : '',
22060
22061     /**
22062      * Protected method that will not generally be called directly. It
22063      * is called when the editor initializes the iframe with HTML contents. Override this method if you
22064      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
22065      */
22066     getDocMarkup : function(){
22067         // body styles..
22068         var st = '';
22069         
22070         // inherit styels from page...?? 
22071         if (this.stylesheets === false) {
22072             
22073             Roo.get(document.head).select('style').each(function(node) {
22074                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22075             });
22076             
22077             Roo.get(document.head).select('link').each(function(node) { 
22078                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
22079             });
22080             
22081         } else if (!this.stylesheets.length) {
22082                 // simple..
22083                 st = '<style type="text/css">' +
22084                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22085                    '</style>';
22086         } else { 
22087             st = '<style type="text/css">' +
22088                     this.stylesheets +
22089                 '</style>';
22090         }
22091         
22092         st +=  '<style type="text/css">' +
22093             'IMG { cursor: pointer } ' +
22094         '</style>';
22095
22096         var cls = 'roo-htmleditor-body';
22097         
22098         if(this.bodyCls.length){
22099             cls += ' ' + this.bodyCls;
22100         }
22101         
22102         return '<html><head>' + st  +
22103             //<style type="text/css">' +
22104             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
22105             //'</style>' +
22106             ' </head><body class="' +  cls + '"></body></html>';
22107     },
22108
22109     // private
22110     onRender : function(ct, position)
22111     {
22112         var _t = this;
22113         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
22114         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
22115         
22116         
22117         this.el.dom.style.border = '0 none';
22118         this.el.dom.setAttribute('tabIndex', -1);
22119         this.el.addClass('x-hidden hide');
22120         
22121         
22122         
22123         if(Roo.isIE){ // fix IE 1px bogus margin
22124             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
22125         }
22126        
22127         
22128         this.frameId = Roo.id();
22129         
22130          
22131         
22132         var iframe = this.owner.wrap.createChild({
22133             tag: 'iframe',
22134             cls: 'form-control', // bootstrap..
22135             id: this.frameId,
22136             name: this.frameId,
22137             frameBorder : 'no',
22138             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
22139         }, this.el
22140         );
22141         
22142         
22143         this.iframe = iframe.dom;
22144
22145          this.assignDocWin();
22146         
22147         this.doc.designMode = 'on';
22148        
22149         this.doc.open();
22150         this.doc.write(this.getDocMarkup());
22151         this.doc.close();
22152
22153         
22154         var task = { // must defer to wait for browser to be ready
22155             run : function(){
22156                 //console.log("run task?" + this.doc.readyState);
22157                 this.assignDocWin();
22158                 if(this.doc.body || this.doc.readyState == 'complete'){
22159                     try {
22160                         this.doc.designMode="on";
22161                     } catch (e) {
22162                         return;
22163                     }
22164                     Roo.TaskMgr.stop(task);
22165                     this.initEditor.defer(10, this);
22166                 }
22167             },
22168             interval : 10,
22169             duration: 10000,
22170             scope: this
22171         };
22172         Roo.TaskMgr.start(task);
22173
22174     },
22175
22176     // private
22177     onResize : function(w, h)
22178     {
22179          Roo.log('resize: ' +w + ',' + h );
22180         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
22181         if(!this.iframe){
22182             return;
22183         }
22184         if(typeof w == 'number'){
22185             
22186             this.iframe.style.width = w + 'px';
22187         }
22188         if(typeof h == 'number'){
22189             
22190             this.iframe.style.height = h + 'px';
22191             if(this.doc){
22192                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
22193             }
22194         }
22195         
22196     },
22197
22198     /**
22199      * Toggles the editor between standard and source edit mode.
22200      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22201      */
22202     toggleSourceEdit : function(sourceEditMode){
22203         
22204         this.sourceEditMode = sourceEditMode === true;
22205         
22206         if(this.sourceEditMode){
22207  
22208             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
22209             
22210         }else{
22211             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
22212             //this.iframe.className = '';
22213             this.deferFocus();
22214         }
22215         //this.setSize(this.owner.wrap.getSize());
22216         //this.fireEvent('editmodechange', this, this.sourceEditMode);
22217     },
22218
22219     
22220   
22221
22222     /**
22223      * Protected method that will not generally be called directly. If you need/want
22224      * custom HTML cleanup, this is the method you should override.
22225      * @param {String} html The HTML to be cleaned
22226      * return {String} The cleaned HTML
22227      */
22228     cleanHtml : function(html){
22229         html = String(html);
22230         if(html.length > 5){
22231             if(Roo.isSafari){ // strip safari nonsense
22232                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
22233             }
22234         }
22235         if(html == '&nbsp;'){
22236             html = '';
22237         }
22238         return html;
22239     },
22240
22241     /**
22242      * HTML Editor -> Textarea
22243      * Protected method that will not generally be called directly. Syncs the contents
22244      * of the editor iframe with the textarea.
22245      */
22246     syncValue : function(){
22247         if(this.initialized){
22248             var bd = (this.doc.body || this.doc.documentElement);
22249             //this.cleanUpPaste(); -- this is done else where and causes havoc..
22250             var html = bd.innerHTML;
22251             if(Roo.isSafari){
22252                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
22253                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
22254                 if(m && m[1]){
22255                     html = '<div style="'+m[0]+'">' + html + '</div>';
22256                 }
22257             }
22258             html = this.cleanHtml(html);
22259             // fix up the special chars.. normaly like back quotes in word...
22260             // however we do not want to do this with chinese..
22261             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
22262                 var cc = b.charCodeAt();
22263                 if (
22264                     (cc >= 0x4E00 && cc < 0xA000 ) ||
22265                     (cc >= 0x3400 && cc < 0x4E00 ) ||
22266                     (cc >= 0xf900 && cc < 0xfb00 )
22267                 ) {
22268                         return b;
22269                 }
22270                 return "&#"+cc+";" 
22271             });
22272             if(this.owner.fireEvent('beforesync', this, html) !== false){
22273                 this.el.dom.value = html;
22274                 this.owner.fireEvent('sync', this, html);
22275             }
22276         }
22277     },
22278
22279     /**
22280      * Protected method that will not generally be called directly. Pushes the value of the textarea
22281      * into the iframe editor.
22282      */
22283     pushValue : function(){
22284         if(this.initialized){
22285             var v = this.el.dom.value.trim();
22286             
22287 //            if(v.length < 1){
22288 //                v = '&#160;';
22289 //            }
22290             
22291             if(this.owner.fireEvent('beforepush', this, v) !== false){
22292                 var d = (this.doc.body || this.doc.documentElement);
22293                 d.innerHTML = v;
22294                 this.cleanUpPaste();
22295                 this.el.dom.value = d.innerHTML;
22296                 this.owner.fireEvent('push', this, v);
22297             }
22298         }
22299     },
22300
22301     // private
22302     deferFocus : function(){
22303         this.focus.defer(10, this);
22304     },
22305
22306     // doc'ed in Field
22307     focus : function(){
22308         if(this.win && !this.sourceEditMode){
22309             this.win.focus();
22310         }else{
22311             this.el.focus();
22312         }
22313     },
22314     
22315     assignDocWin: function()
22316     {
22317         var iframe = this.iframe;
22318         
22319          if(Roo.isIE){
22320             this.doc = iframe.contentWindow.document;
22321             this.win = iframe.contentWindow;
22322         } else {
22323 //            if (!Roo.get(this.frameId)) {
22324 //                return;
22325 //            }
22326 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22327 //            this.win = Roo.get(this.frameId).dom.contentWindow;
22328             
22329             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
22330                 return;
22331             }
22332             
22333             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
22334             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
22335         }
22336     },
22337     
22338     // private
22339     initEditor : function(){
22340         //console.log("INIT EDITOR");
22341         this.assignDocWin();
22342         
22343         
22344         
22345         this.doc.designMode="on";
22346         this.doc.open();
22347         this.doc.write(this.getDocMarkup());
22348         this.doc.close();
22349         
22350         var dbody = (this.doc.body || this.doc.documentElement);
22351         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
22352         // this copies styles from the containing element into thsi one..
22353         // not sure why we need all of this..
22354         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
22355         
22356         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
22357         //ss['background-attachment'] = 'fixed'; // w3c
22358         dbody.bgProperties = 'fixed'; // ie
22359         //Roo.DomHelper.applyStyles(dbody, ss);
22360         Roo.EventManager.on(this.doc, {
22361             //'mousedown': this.onEditorEvent,
22362             'mouseup': this.onEditorEvent,
22363             'dblclick': this.onEditorEvent,
22364             'click': this.onEditorEvent,
22365             'keyup': this.onEditorEvent,
22366             buffer:100,
22367             scope: this
22368         });
22369         if(Roo.isGecko){
22370             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
22371         }
22372         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
22373             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
22374         }
22375         this.initialized = true;
22376
22377         this.owner.fireEvent('initialize', this);
22378         this.pushValue();
22379     },
22380
22381     // private
22382     onDestroy : function(){
22383         
22384         
22385         
22386         if(this.rendered){
22387             
22388             //for (var i =0; i < this.toolbars.length;i++) {
22389             //    // fixme - ask toolbars for heights?
22390             //    this.toolbars[i].onDestroy();
22391            // }
22392             
22393             //this.wrap.dom.innerHTML = '';
22394             //this.wrap.remove();
22395         }
22396     },
22397
22398     // private
22399     onFirstFocus : function(){
22400         
22401         this.assignDocWin();
22402         
22403         
22404         this.activated = true;
22405          
22406     
22407         if(Roo.isGecko){ // prevent silly gecko errors
22408             this.win.focus();
22409             var s = this.win.getSelection();
22410             if(!s.focusNode || s.focusNode.nodeType != 3){
22411                 var r = s.getRangeAt(0);
22412                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
22413                 r.collapse(true);
22414                 this.deferFocus();
22415             }
22416             try{
22417                 this.execCmd('useCSS', true);
22418                 this.execCmd('styleWithCSS', false);
22419             }catch(e){}
22420         }
22421         this.owner.fireEvent('activate', this);
22422     },
22423
22424     // private
22425     adjustFont: function(btn){
22426         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
22427         //if(Roo.isSafari){ // safari
22428         //    adjust *= 2;
22429        // }
22430         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
22431         if(Roo.isSafari){ // safari
22432             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
22433             v =  (v < 10) ? 10 : v;
22434             v =  (v > 48) ? 48 : v;
22435             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
22436             
22437         }
22438         
22439         
22440         v = Math.max(1, v+adjust);
22441         
22442         this.execCmd('FontSize', v  );
22443     },
22444
22445     onEditorEvent : function(e)
22446     {
22447         this.owner.fireEvent('editorevent', this, e);
22448       //  this.updateToolbar();
22449         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
22450     },
22451
22452     insertTag : function(tg)
22453     {
22454         // could be a bit smarter... -> wrap the current selected tRoo..
22455         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
22456             
22457             range = this.createRange(this.getSelection());
22458             var wrappingNode = this.doc.createElement(tg.toLowerCase());
22459             wrappingNode.appendChild(range.extractContents());
22460             range.insertNode(wrappingNode);
22461
22462             return;
22463             
22464             
22465             
22466         }
22467         this.execCmd("formatblock",   tg);
22468         
22469     },
22470     
22471     insertText : function(txt)
22472     {
22473         
22474         
22475         var range = this.createRange();
22476         range.deleteContents();
22477                //alert(Sender.getAttribute('label'));
22478                
22479         range.insertNode(this.doc.createTextNode(txt));
22480     } ,
22481     
22482      
22483
22484     /**
22485      * Executes a Midas editor command on the editor document and performs necessary focus and
22486      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
22487      * @param {String} cmd The Midas command
22488      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22489      */
22490     relayCmd : function(cmd, value){
22491         this.win.focus();
22492         this.execCmd(cmd, value);
22493         this.owner.fireEvent('editorevent', this);
22494         //this.updateToolbar();
22495         this.owner.deferFocus();
22496     },
22497
22498     /**
22499      * Executes a Midas editor command directly on the editor document.
22500      * For visual commands, you should use {@link #relayCmd} instead.
22501      * <b>This should only be called after the editor is initialized.</b>
22502      * @param {String} cmd The Midas command
22503      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
22504      */
22505     execCmd : function(cmd, value){
22506         this.doc.execCommand(cmd, false, value === undefined ? null : value);
22507         this.syncValue();
22508     },
22509  
22510  
22511    
22512     /**
22513      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
22514      * to insert tRoo.
22515      * @param {String} text | dom node.. 
22516      */
22517     insertAtCursor : function(text)
22518     {
22519         
22520         if(!this.activated){
22521             return;
22522         }
22523         /*
22524         if(Roo.isIE){
22525             this.win.focus();
22526             var r = this.doc.selection.createRange();
22527             if(r){
22528                 r.collapse(true);
22529                 r.pasteHTML(text);
22530                 this.syncValue();
22531                 this.deferFocus();
22532             
22533             }
22534             return;
22535         }
22536         */
22537         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
22538             this.win.focus();
22539             
22540             
22541             // from jquery ui (MIT licenced)
22542             var range, node;
22543             var win = this.win;
22544             
22545             if (win.getSelection && win.getSelection().getRangeAt) {
22546                 range = win.getSelection().getRangeAt(0);
22547                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
22548                 range.insertNode(node);
22549             } else if (win.document.selection && win.document.selection.createRange) {
22550                 // no firefox support
22551                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22552                 win.document.selection.createRange().pasteHTML(txt);
22553             } else {
22554                 // no firefox support
22555                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
22556                 this.execCmd('InsertHTML', txt);
22557             } 
22558             
22559             this.syncValue();
22560             
22561             this.deferFocus();
22562         }
22563     },
22564  // private
22565     mozKeyPress : function(e){
22566         if(e.ctrlKey){
22567             var c = e.getCharCode(), cmd;
22568           
22569             if(c > 0){
22570                 c = String.fromCharCode(c).toLowerCase();
22571                 switch(c){
22572                     case 'b':
22573                         cmd = 'bold';
22574                         break;
22575                     case 'i':
22576                         cmd = 'italic';
22577                         break;
22578                     
22579                     case 'u':
22580                         cmd = 'underline';
22581                         break;
22582                     
22583                     case 'v':
22584                         this.cleanUpPaste.defer(100, this);
22585                         return;
22586                         
22587                 }
22588                 if(cmd){
22589                     this.win.focus();
22590                     this.execCmd(cmd);
22591                     this.deferFocus();
22592                     e.preventDefault();
22593                 }
22594                 
22595             }
22596         }
22597     },
22598
22599     // private
22600     fixKeys : function(){ // load time branching for fastest keydown performance
22601         if(Roo.isIE){
22602             return function(e){
22603                 var k = e.getKey(), r;
22604                 if(k == e.TAB){
22605                     e.stopEvent();
22606                     r = this.doc.selection.createRange();
22607                     if(r){
22608                         r.collapse(true);
22609                         r.pasteHTML('&#160;&#160;&#160;&#160;');
22610                         this.deferFocus();
22611                     }
22612                     return;
22613                 }
22614                 
22615                 if(k == e.ENTER){
22616                     r = this.doc.selection.createRange();
22617                     if(r){
22618                         var target = r.parentElement();
22619                         if(!target || target.tagName.toLowerCase() != 'li'){
22620                             e.stopEvent();
22621                             r.pasteHTML('<br />');
22622                             r.collapse(false);
22623                             r.select();
22624                         }
22625                     }
22626                 }
22627                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22628                     this.cleanUpPaste.defer(100, this);
22629                     return;
22630                 }
22631                 
22632                 
22633             };
22634         }else if(Roo.isOpera){
22635             return function(e){
22636                 var k = e.getKey();
22637                 if(k == e.TAB){
22638                     e.stopEvent();
22639                     this.win.focus();
22640                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
22641                     this.deferFocus();
22642                 }
22643                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22644                     this.cleanUpPaste.defer(100, this);
22645                     return;
22646                 }
22647                 
22648             };
22649         }else if(Roo.isSafari){
22650             return function(e){
22651                 var k = e.getKey();
22652                 
22653                 if(k == e.TAB){
22654                     e.stopEvent();
22655                     this.execCmd('InsertText','\t');
22656                     this.deferFocus();
22657                     return;
22658                 }
22659                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22660                     this.cleanUpPaste.defer(100, this);
22661                     return;
22662                 }
22663                 
22664              };
22665         }
22666     }(),
22667     
22668     getAllAncestors: function()
22669     {
22670         var p = this.getSelectedNode();
22671         var a = [];
22672         if (!p) {
22673             a.push(p); // push blank onto stack..
22674             p = this.getParentElement();
22675         }
22676         
22677         
22678         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22679             a.push(p);
22680             p = p.parentNode;
22681         }
22682         a.push(this.doc.body);
22683         return a;
22684     },
22685     lastSel : false,
22686     lastSelNode : false,
22687     
22688     
22689     getSelection : function() 
22690     {
22691         this.assignDocWin();
22692         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22693     },
22694     
22695     getSelectedNode: function() 
22696     {
22697         // this may only work on Gecko!!!
22698         
22699         // should we cache this!!!!
22700         
22701         
22702         
22703          
22704         var range = this.createRange(this.getSelection()).cloneRange();
22705         
22706         if (Roo.isIE) {
22707             var parent = range.parentElement();
22708             while (true) {
22709                 var testRange = range.duplicate();
22710                 testRange.moveToElementText(parent);
22711                 if (testRange.inRange(range)) {
22712                     break;
22713                 }
22714                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22715                     break;
22716                 }
22717                 parent = parent.parentElement;
22718             }
22719             return parent;
22720         }
22721         
22722         // is ancestor a text element.
22723         var ac =  range.commonAncestorContainer;
22724         if (ac.nodeType == 3) {
22725             ac = ac.parentNode;
22726         }
22727         
22728         var ar = ac.childNodes;
22729          
22730         var nodes = [];
22731         var other_nodes = [];
22732         var has_other_nodes = false;
22733         for (var i=0;i<ar.length;i++) {
22734             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22735                 continue;
22736             }
22737             // fullly contained node.
22738             
22739             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22740                 nodes.push(ar[i]);
22741                 continue;
22742             }
22743             
22744             // probably selected..
22745             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22746                 other_nodes.push(ar[i]);
22747                 continue;
22748             }
22749             // outer..
22750             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22751                 continue;
22752             }
22753             
22754             
22755             has_other_nodes = true;
22756         }
22757         if (!nodes.length && other_nodes.length) {
22758             nodes= other_nodes;
22759         }
22760         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22761             return false;
22762         }
22763         
22764         return nodes[0];
22765     },
22766     createRange: function(sel)
22767     {
22768         // this has strange effects when using with 
22769         // top toolbar - not sure if it's a great idea.
22770         //this.editor.contentWindow.focus();
22771         if (typeof sel != "undefined") {
22772             try {
22773                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22774             } catch(e) {
22775                 return this.doc.createRange();
22776             }
22777         } else {
22778             return this.doc.createRange();
22779         }
22780     },
22781     getParentElement: function()
22782     {
22783         
22784         this.assignDocWin();
22785         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22786         
22787         var range = this.createRange(sel);
22788          
22789         try {
22790             var p = range.commonAncestorContainer;
22791             while (p.nodeType == 3) { // text node
22792                 p = p.parentNode;
22793             }
22794             return p;
22795         } catch (e) {
22796             return null;
22797         }
22798     
22799     },
22800     /***
22801      *
22802      * Range intersection.. the hard stuff...
22803      *  '-1' = before
22804      *  '0' = hits..
22805      *  '1' = after.
22806      *         [ -- selected range --- ]
22807      *   [fail]                        [fail]
22808      *
22809      *    basically..
22810      *      if end is before start or  hits it. fail.
22811      *      if start is after end or hits it fail.
22812      *
22813      *   if either hits (but other is outside. - then it's not 
22814      *   
22815      *    
22816      **/
22817     
22818     
22819     // @see http://www.thismuchiknow.co.uk/?p=64.
22820     rangeIntersectsNode : function(range, node)
22821     {
22822         var nodeRange = node.ownerDocument.createRange();
22823         try {
22824             nodeRange.selectNode(node);
22825         } catch (e) {
22826             nodeRange.selectNodeContents(node);
22827         }
22828     
22829         var rangeStartRange = range.cloneRange();
22830         rangeStartRange.collapse(true);
22831     
22832         var rangeEndRange = range.cloneRange();
22833         rangeEndRange.collapse(false);
22834     
22835         var nodeStartRange = nodeRange.cloneRange();
22836         nodeStartRange.collapse(true);
22837     
22838         var nodeEndRange = nodeRange.cloneRange();
22839         nodeEndRange.collapse(false);
22840     
22841         return rangeStartRange.compareBoundaryPoints(
22842                  Range.START_TO_START, nodeEndRange) == -1 &&
22843                rangeEndRange.compareBoundaryPoints(
22844                  Range.START_TO_START, nodeStartRange) == 1;
22845         
22846          
22847     },
22848     rangeCompareNode : function(range, node)
22849     {
22850         var nodeRange = node.ownerDocument.createRange();
22851         try {
22852             nodeRange.selectNode(node);
22853         } catch (e) {
22854             nodeRange.selectNodeContents(node);
22855         }
22856         
22857         
22858         range.collapse(true);
22859     
22860         nodeRange.collapse(true);
22861      
22862         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22863         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22864          
22865         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22866         
22867         var nodeIsBefore   =  ss == 1;
22868         var nodeIsAfter    = ee == -1;
22869         
22870         if (nodeIsBefore && nodeIsAfter) {
22871             return 0; // outer
22872         }
22873         if (!nodeIsBefore && nodeIsAfter) {
22874             return 1; //right trailed.
22875         }
22876         
22877         if (nodeIsBefore && !nodeIsAfter) {
22878             return 2;  // left trailed.
22879         }
22880         // fully contined.
22881         return 3;
22882     },
22883
22884     // private? - in a new class?
22885     cleanUpPaste :  function()
22886     {
22887         // cleans up the whole document..
22888         Roo.log('cleanuppaste');
22889         
22890         this.cleanUpChildren(this.doc.body);
22891         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22892         if (clean != this.doc.body.innerHTML) {
22893             this.doc.body.innerHTML = clean;
22894         }
22895         
22896     },
22897     
22898     cleanWordChars : function(input) {// change the chars to hex code
22899         var he = Roo.HtmlEditorCore;
22900         
22901         var output = input;
22902         Roo.each(he.swapCodes, function(sw) { 
22903             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22904             
22905             output = output.replace(swapper, sw[1]);
22906         });
22907         
22908         return output;
22909     },
22910     
22911     
22912     cleanUpChildren : function (n)
22913     {
22914         if (!n.childNodes.length) {
22915             return;
22916         }
22917         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22918            this.cleanUpChild(n.childNodes[i]);
22919         }
22920     },
22921     
22922     
22923         
22924     
22925     cleanUpChild : function (node)
22926     {
22927         var ed = this;
22928         //console.log(node);
22929         if (node.nodeName == "#text") {
22930             // clean up silly Windows -- stuff?
22931             return; 
22932         }
22933         if (node.nodeName == "#comment") {
22934             node.parentNode.removeChild(node);
22935             // clean up silly Windows -- stuff?
22936             return; 
22937         }
22938         var lcname = node.tagName.toLowerCase();
22939         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22940         // whitelist of tags..
22941         
22942         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22943             // remove node.
22944             node.parentNode.removeChild(node);
22945             return;
22946             
22947         }
22948         
22949         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22950         
22951         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22952         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22953         
22954         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22955         //    remove_keep_children = true;
22956         //}
22957         
22958         if (remove_keep_children) {
22959             this.cleanUpChildren(node);
22960             // inserts everything just before this node...
22961             while (node.childNodes.length) {
22962                 var cn = node.childNodes[0];
22963                 node.removeChild(cn);
22964                 node.parentNode.insertBefore(cn, node);
22965             }
22966             node.parentNode.removeChild(node);
22967             return;
22968         }
22969         
22970         if (!node.attributes || !node.attributes.length) {
22971             this.cleanUpChildren(node);
22972             return;
22973         }
22974         
22975         function cleanAttr(n,v)
22976         {
22977             
22978             if (v.match(/^\./) || v.match(/^\//)) {
22979                 return;
22980             }
22981             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
22982                 return;
22983             }
22984             if (v.match(/^#/)) {
22985                 return;
22986             }
22987 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22988             node.removeAttribute(n);
22989             
22990         }
22991         
22992         var cwhite = this.cwhite;
22993         var cblack = this.cblack;
22994             
22995         function cleanStyle(n,v)
22996         {
22997             if (v.match(/expression/)) { //XSS?? should we even bother..
22998                 node.removeAttribute(n);
22999                 return;
23000             }
23001             
23002             var parts = v.split(/;/);
23003             var clean = [];
23004             
23005             Roo.each(parts, function(p) {
23006                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
23007                 if (!p.length) {
23008                     return true;
23009                 }
23010                 var l = p.split(':').shift().replace(/\s+/g,'');
23011                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
23012                 
23013                 if ( cwhite.length && cblack.indexOf(l) > -1) {
23014 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23015                     //node.removeAttribute(n);
23016                     return true;
23017                 }
23018                 //Roo.log()
23019                 // only allow 'c whitelisted system attributes'
23020                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
23021 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
23022                     //node.removeAttribute(n);
23023                     return true;
23024                 }
23025                 
23026                 
23027                  
23028                 
23029                 clean.push(p);
23030                 return true;
23031             });
23032             if (clean.length) { 
23033                 node.setAttribute(n, clean.join(';'));
23034             } else {
23035                 node.removeAttribute(n);
23036             }
23037             
23038         }
23039         
23040         
23041         for (var i = node.attributes.length-1; i > -1 ; i--) {
23042             var a = node.attributes[i];
23043             //console.log(a);
23044             
23045             if (a.name.toLowerCase().substr(0,2)=='on')  {
23046                 node.removeAttribute(a.name);
23047                 continue;
23048             }
23049             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
23050                 node.removeAttribute(a.name);
23051                 continue;
23052             }
23053             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
23054                 cleanAttr(a.name,a.value); // fixme..
23055                 continue;
23056             }
23057             if (a.name == 'style') {
23058                 cleanStyle(a.name,a.value);
23059                 continue;
23060             }
23061             /// clean up MS crap..
23062             // tecnically this should be a list of valid class'es..
23063             
23064             
23065             if (a.name == 'class') {
23066                 if (a.value.match(/^Mso/)) {
23067                     node.className = '';
23068                 }
23069                 
23070                 if (a.value.match(/^body$/)) {
23071                     node.className = '';
23072                 }
23073                 continue;
23074             }
23075             
23076             // style cleanup!?
23077             // class cleanup?
23078             
23079         }
23080         
23081         
23082         this.cleanUpChildren(node);
23083         
23084         
23085     },
23086     
23087     /**
23088      * Clean up MS wordisms...
23089      */
23090     cleanWord : function(node)
23091     {
23092         
23093         
23094         if (!node) {
23095             this.cleanWord(this.doc.body);
23096             return;
23097         }
23098         if (node.nodeName == "#text") {
23099             // clean up silly Windows -- stuff?
23100             return; 
23101         }
23102         if (node.nodeName == "#comment") {
23103             node.parentNode.removeChild(node);
23104             // clean up silly Windows -- stuff?
23105             return; 
23106         }
23107         
23108         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
23109             node.parentNode.removeChild(node);
23110             return;
23111         }
23112         
23113         // remove - but keep children..
23114         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
23115             while (node.childNodes.length) {
23116                 var cn = node.childNodes[0];
23117                 node.removeChild(cn);
23118                 node.parentNode.insertBefore(cn, node);
23119             }
23120             node.parentNode.removeChild(node);
23121             this.iterateChildren(node, this.cleanWord);
23122             return;
23123         }
23124         // clean styles
23125         if (node.className.length) {
23126             
23127             var cn = node.className.split(/\W+/);
23128             var cna = [];
23129             Roo.each(cn, function(cls) {
23130                 if (cls.match(/Mso[a-zA-Z]+/)) {
23131                     return;
23132                 }
23133                 cna.push(cls);
23134             });
23135             node.className = cna.length ? cna.join(' ') : '';
23136             if (!cna.length) {
23137                 node.removeAttribute("class");
23138             }
23139         }
23140         
23141         if (node.hasAttribute("lang")) {
23142             node.removeAttribute("lang");
23143         }
23144         
23145         if (node.hasAttribute("style")) {
23146             
23147             var styles = node.getAttribute("style").split(";");
23148             var nstyle = [];
23149             Roo.each(styles, function(s) {
23150                 if (!s.match(/:/)) {
23151                     return;
23152                 }
23153                 var kv = s.split(":");
23154                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
23155                     return;
23156                 }
23157                 // what ever is left... we allow.
23158                 nstyle.push(s);
23159             });
23160             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23161             if (!nstyle.length) {
23162                 node.removeAttribute('style');
23163             }
23164         }
23165         this.iterateChildren(node, this.cleanWord);
23166         
23167         
23168         
23169     },
23170     /**
23171      * iterateChildren of a Node, calling fn each time, using this as the scole..
23172      * @param {DomNode} node node to iterate children of.
23173      * @param {Function} fn method of this class to call on each item.
23174      */
23175     iterateChildren : function(node, fn)
23176     {
23177         if (!node.childNodes.length) {
23178                 return;
23179         }
23180         for (var i = node.childNodes.length-1; i > -1 ; i--) {
23181            fn.call(this, node.childNodes[i])
23182         }
23183     },
23184     
23185     
23186     /**
23187      * cleanTableWidths.
23188      *
23189      * Quite often pasting from word etc.. results in tables with column and widths.
23190      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
23191      *
23192      */
23193     cleanTableWidths : function(node)
23194     {
23195          
23196          
23197         if (!node) {
23198             this.cleanTableWidths(this.doc.body);
23199             return;
23200         }
23201         
23202         // ignore list...
23203         if (node.nodeName == "#text" || node.nodeName == "#comment") {
23204             return; 
23205         }
23206         Roo.log(node.tagName);
23207         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
23208             this.iterateChildren(node, this.cleanTableWidths);
23209             return;
23210         }
23211         if (node.hasAttribute('width')) {
23212             node.removeAttribute('width');
23213         }
23214         
23215          
23216         if (node.hasAttribute("style")) {
23217             // pretty basic...
23218             
23219             var styles = node.getAttribute("style").split(";");
23220             var nstyle = [];
23221             Roo.each(styles, function(s) {
23222                 if (!s.match(/:/)) {
23223                     return;
23224                 }
23225                 var kv = s.split(":");
23226                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
23227                     return;
23228                 }
23229                 // what ever is left... we allow.
23230                 nstyle.push(s);
23231             });
23232             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
23233             if (!nstyle.length) {
23234                 node.removeAttribute('style');
23235             }
23236         }
23237         
23238         this.iterateChildren(node, this.cleanTableWidths);
23239         
23240         
23241     },
23242     
23243     
23244     
23245     
23246     domToHTML : function(currentElement, depth, nopadtext) {
23247         
23248         depth = depth || 0;
23249         nopadtext = nopadtext || false;
23250     
23251         if (!currentElement) {
23252             return this.domToHTML(this.doc.body);
23253         }
23254         
23255         //Roo.log(currentElement);
23256         var j;
23257         var allText = false;
23258         var nodeName = currentElement.nodeName;
23259         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
23260         
23261         if  (nodeName == '#text') {
23262             
23263             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
23264         }
23265         
23266         
23267         var ret = '';
23268         if (nodeName != 'BODY') {
23269              
23270             var i = 0;
23271             // Prints the node tagName, such as <A>, <IMG>, etc
23272             if (tagName) {
23273                 var attr = [];
23274                 for(i = 0; i < currentElement.attributes.length;i++) {
23275                     // quoting?
23276                     var aname = currentElement.attributes.item(i).name;
23277                     if (!currentElement.attributes.item(i).value.length) {
23278                         continue;
23279                     }
23280                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
23281                 }
23282                 
23283                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
23284             } 
23285             else {
23286                 
23287                 // eack
23288             }
23289         } else {
23290             tagName = false;
23291         }
23292         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
23293             return ret;
23294         }
23295         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
23296             nopadtext = true;
23297         }
23298         
23299         
23300         // Traverse the tree
23301         i = 0;
23302         var currentElementChild = currentElement.childNodes.item(i);
23303         var allText = true;
23304         var innerHTML  = '';
23305         lastnode = '';
23306         while (currentElementChild) {
23307             // Formatting code (indent the tree so it looks nice on the screen)
23308             var nopad = nopadtext;
23309             if (lastnode == 'SPAN') {
23310                 nopad  = true;
23311             }
23312             // text
23313             if  (currentElementChild.nodeName == '#text') {
23314                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
23315                 toadd = nopadtext ? toadd : toadd.trim();
23316                 if (!nopad && toadd.length > 80) {
23317                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
23318                 }
23319                 innerHTML  += toadd;
23320                 
23321                 i++;
23322                 currentElementChild = currentElement.childNodes.item(i);
23323                 lastNode = '';
23324                 continue;
23325             }
23326             allText = false;
23327             
23328             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
23329                 
23330             // Recursively traverse the tree structure of the child node
23331             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
23332             lastnode = currentElementChild.nodeName;
23333             i++;
23334             currentElementChild=currentElement.childNodes.item(i);
23335         }
23336         
23337         ret += innerHTML;
23338         
23339         if (!allText) {
23340                 // The remaining code is mostly for formatting the tree
23341             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
23342         }
23343         
23344         
23345         if (tagName) {
23346             ret+= "</"+tagName+">";
23347         }
23348         return ret;
23349         
23350     },
23351         
23352     applyBlacklists : function()
23353     {
23354         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
23355         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
23356         
23357         this.white = [];
23358         this.black = [];
23359         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
23360             if (b.indexOf(tag) > -1) {
23361                 return;
23362             }
23363             this.white.push(tag);
23364             
23365         }, this);
23366         
23367         Roo.each(w, function(tag) {
23368             if (b.indexOf(tag) > -1) {
23369                 return;
23370             }
23371             if (this.white.indexOf(tag) > -1) {
23372                 return;
23373             }
23374             this.white.push(tag);
23375             
23376         }, this);
23377         
23378         
23379         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
23380             if (w.indexOf(tag) > -1) {
23381                 return;
23382             }
23383             this.black.push(tag);
23384             
23385         }, this);
23386         
23387         Roo.each(b, function(tag) {
23388             if (w.indexOf(tag) > -1) {
23389                 return;
23390             }
23391             if (this.black.indexOf(tag) > -1) {
23392                 return;
23393             }
23394             this.black.push(tag);
23395             
23396         }, this);
23397         
23398         
23399         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
23400         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
23401         
23402         this.cwhite = [];
23403         this.cblack = [];
23404         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
23405             if (b.indexOf(tag) > -1) {
23406                 return;
23407             }
23408             this.cwhite.push(tag);
23409             
23410         }, this);
23411         
23412         Roo.each(w, function(tag) {
23413             if (b.indexOf(tag) > -1) {
23414                 return;
23415             }
23416             if (this.cwhite.indexOf(tag) > -1) {
23417                 return;
23418             }
23419             this.cwhite.push(tag);
23420             
23421         }, this);
23422         
23423         
23424         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
23425             if (w.indexOf(tag) > -1) {
23426                 return;
23427             }
23428             this.cblack.push(tag);
23429             
23430         }, this);
23431         
23432         Roo.each(b, function(tag) {
23433             if (w.indexOf(tag) > -1) {
23434                 return;
23435             }
23436             if (this.cblack.indexOf(tag) > -1) {
23437                 return;
23438             }
23439             this.cblack.push(tag);
23440             
23441         }, this);
23442     },
23443     
23444     setStylesheets : function(stylesheets)
23445     {
23446         if(typeof(stylesheets) == 'string'){
23447             Roo.get(this.iframe.contentDocument.head).createChild({
23448                 tag : 'link',
23449                 rel : 'stylesheet',
23450                 type : 'text/css',
23451                 href : stylesheets
23452             });
23453             
23454             return;
23455         }
23456         var _this = this;
23457      
23458         Roo.each(stylesheets, function(s) {
23459             if(!s.length){
23460                 return;
23461             }
23462             
23463             Roo.get(_this.iframe.contentDocument.head).createChild({
23464                 tag : 'link',
23465                 rel : 'stylesheet',
23466                 type : 'text/css',
23467                 href : s
23468             });
23469         });
23470
23471         
23472     },
23473     
23474     removeStylesheets : function()
23475     {
23476         var _this = this;
23477         
23478         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
23479             s.remove();
23480         });
23481     },
23482     
23483     setStyle : function(style)
23484     {
23485         Roo.get(this.iframe.contentDocument.head).createChild({
23486             tag : 'style',
23487             type : 'text/css',
23488             html : style
23489         });
23490
23491         return;
23492     }
23493     
23494     // hide stuff that is not compatible
23495     /**
23496      * @event blur
23497      * @hide
23498      */
23499     /**
23500      * @event change
23501      * @hide
23502      */
23503     /**
23504      * @event focus
23505      * @hide
23506      */
23507     /**
23508      * @event specialkey
23509      * @hide
23510      */
23511     /**
23512      * @cfg {String} fieldClass @hide
23513      */
23514     /**
23515      * @cfg {String} focusClass @hide
23516      */
23517     /**
23518      * @cfg {String} autoCreate @hide
23519      */
23520     /**
23521      * @cfg {String} inputType @hide
23522      */
23523     /**
23524      * @cfg {String} invalidClass @hide
23525      */
23526     /**
23527      * @cfg {String} invalidText @hide
23528      */
23529     /**
23530      * @cfg {String} msgFx @hide
23531      */
23532     /**
23533      * @cfg {String} validateOnBlur @hide
23534      */
23535 });
23536
23537 Roo.HtmlEditorCore.white = [
23538         'area', 'br', 'img', 'input', 'hr', 'wbr',
23539         
23540        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
23541        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
23542        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
23543        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
23544        'table',   'ul',         'xmp', 
23545        
23546        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
23547       'thead',   'tr', 
23548      
23549       'dir', 'menu', 'ol', 'ul', 'dl',
23550        
23551       'embed',  'object'
23552 ];
23553
23554
23555 Roo.HtmlEditorCore.black = [
23556     //    'embed',  'object', // enable - backend responsiblity to clean thiese
23557         'applet', // 
23558         'base',   'basefont', 'bgsound', 'blink',  'body', 
23559         'frame',  'frameset', 'head',    'html',   'ilayer', 
23560         'iframe', 'layer',  'link',     'meta',    'object',   
23561         'script', 'style' ,'title',  'xml' // clean later..
23562 ];
23563 Roo.HtmlEditorCore.clean = [
23564     'script', 'style', 'title', 'xml'
23565 ];
23566 Roo.HtmlEditorCore.remove = [
23567     'font'
23568 ];
23569 // attributes..
23570
23571 Roo.HtmlEditorCore.ablack = [
23572     'on'
23573 ];
23574     
23575 Roo.HtmlEditorCore.aclean = [ 
23576     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
23577 ];
23578
23579 // protocols..
23580 Roo.HtmlEditorCore.pwhite= [
23581         'http',  'https',  'mailto'
23582 ];
23583
23584 // white listed style attributes.
23585 Roo.HtmlEditorCore.cwhite= [
23586       //  'text-align', /// default is to allow most things..
23587       
23588          
23589 //        'font-size'//??
23590 ];
23591
23592 // black listed style attributes.
23593 Roo.HtmlEditorCore.cblack= [
23594       //  'font-size' -- this can be set by the project 
23595 ];
23596
23597
23598 Roo.HtmlEditorCore.swapCodes   =[ 
23599     [    8211, "--" ], 
23600     [    8212, "--" ], 
23601     [    8216,  "'" ],  
23602     [    8217, "'" ],  
23603     [    8220, '"' ],  
23604     [    8221, '"' ],  
23605     [    8226, "*" ],  
23606     [    8230, "..." ]
23607 ]; 
23608
23609     /*
23610  * - LGPL
23611  *
23612  * HtmlEditor
23613  * 
23614  */
23615
23616 /**
23617  * @class Roo.bootstrap.HtmlEditor
23618  * @extends Roo.bootstrap.TextArea
23619  * Bootstrap HtmlEditor class
23620
23621  * @constructor
23622  * Create a new HtmlEditor
23623  * @param {Object} config The config object
23624  */
23625
23626 Roo.bootstrap.HtmlEditor = function(config){
23627     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
23628     if (!this.toolbars) {
23629         this.toolbars = [];
23630     }
23631     
23632     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
23633     this.addEvents({
23634             /**
23635              * @event initialize
23636              * Fires when the editor is fully initialized (including the iframe)
23637              * @param {HtmlEditor} this
23638              */
23639             initialize: true,
23640             /**
23641              * @event activate
23642              * Fires when the editor is first receives the focus. Any insertion must wait
23643              * until after this event.
23644              * @param {HtmlEditor} this
23645              */
23646             activate: true,
23647              /**
23648              * @event beforesync
23649              * Fires before the textarea is updated with content from the editor iframe. Return false
23650              * to cancel the sync.
23651              * @param {HtmlEditor} this
23652              * @param {String} html
23653              */
23654             beforesync: true,
23655              /**
23656              * @event beforepush
23657              * Fires before the iframe editor is updated with content from the textarea. Return false
23658              * to cancel the push.
23659              * @param {HtmlEditor} this
23660              * @param {String} html
23661              */
23662             beforepush: true,
23663              /**
23664              * @event sync
23665              * Fires when the textarea is updated with content from the editor iframe.
23666              * @param {HtmlEditor} this
23667              * @param {String} html
23668              */
23669             sync: true,
23670              /**
23671              * @event push
23672              * Fires when the iframe editor is updated with content from the textarea.
23673              * @param {HtmlEditor} this
23674              * @param {String} html
23675              */
23676             push: true,
23677              /**
23678              * @event editmodechange
23679              * Fires when the editor switches edit modes
23680              * @param {HtmlEditor} this
23681              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23682              */
23683             editmodechange: true,
23684             /**
23685              * @event editorevent
23686              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23687              * @param {HtmlEditor} this
23688              */
23689             editorevent: true,
23690             /**
23691              * @event firstfocus
23692              * Fires when on first focus - needed by toolbars..
23693              * @param {HtmlEditor} this
23694              */
23695             firstfocus: true,
23696             /**
23697              * @event autosave
23698              * Auto save the htmlEditor value as a file into Events
23699              * @param {HtmlEditor} this
23700              */
23701             autosave: true,
23702             /**
23703              * @event savedpreview
23704              * preview the saved version of htmlEditor
23705              * @param {HtmlEditor} this
23706              */
23707             savedpreview: true
23708         });
23709 };
23710
23711
23712 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23713     
23714     
23715       /**
23716      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23717      */
23718     toolbars : false,
23719     
23720      /**
23721     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23722     */
23723     btns : [],
23724    
23725      /**
23726      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23727      *                        Roo.resizable.
23728      */
23729     resizable : false,
23730      /**
23731      * @cfg {Number} height (in pixels)
23732      */   
23733     height: 300,
23734    /**
23735      * @cfg {Number} width (in pixels)
23736      */   
23737     width: false,
23738     
23739     /**
23740      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23741      * 
23742      */
23743     stylesheets: false,
23744     
23745     // id of frame..
23746     frameId: false,
23747     
23748     // private properties
23749     validationEvent : false,
23750     deferHeight: true,
23751     initialized : false,
23752     activated : false,
23753     
23754     onFocus : Roo.emptyFn,
23755     iframePad:3,
23756     hideMode:'offsets',
23757     
23758     tbContainer : false,
23759     
23760     bodyCls : '',
23761     
23762     toolbarContainer :function() {
23763         return this.wrap.select('.x-html-editor-tb',true).first();
23764     },
23765
23766     /**
23767      * Protected method that will not generally be called directly. It
23768      * is called when the editor creates its toolbar. Override this method if you need to
23769      * add custom toolbar buttons.
23770      * @param {HtmlEditor} editor
23771      */
23772     createToolbar : function(){
23773         Roo.log('renewing');
23774         Roo.log("create toolbars");
23775         
23776         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23777         this.toolbars[0].render(this.toolbarContainer());
23778         
23779         return;
23780         
23781 //        if (!editor.toolbars || !editor.toolbars.length) {
23782 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23783 //        }
23784 //        
23785 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23786 //            editor.toolbars[i] = Roo.factory(
23787 //                    typeof(editor.toolbars[i]) == 'string' ?
23788 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23789 //                Roo.bootstrap.HtmlEditor);
23790 //            editor.toolbars[i].init(editor);
23791 //        }
23792     },
23793
23794      
23795     // private
23796     onRender : function(ct, position)
23797     {
23798        // Roo.log("Call onRender: " + this.xtype);
23799         var _t = this;
23800         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23801       
23802         this.wrap = this.inputEl().wrap({
23803             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23804         });
23805         
23806         this.editorcore.onRender(ct, position);
23807          
23808         if (this.resizable) {
23809             this.resizeEl = new Roo.Resizable(this.wrap, {
23810                 pinned : true,
23811                 wrap: true,
23812                 dynamic : true,
23813                 minHeight : this.height,
23814                 height: this.height,
23815                 handles : this.resizable,
23816                 width: this.width,
23817                 listeners : {
23818                     resize : function(r, w, h) {
23819                         _t.onResize(w,h); // -something
23820                     }
23821                 }
23822             });
23823             
23824         }
23825         this.createToolbar(this);
23826        
23827         
23828         if(!this.width && this.resizable){
23829             this.setSize(this.wrap.getSize());
23830         }
23831         if (this.resizeEl) {
23832             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23833             // should trigger onReize..
23834         }
23835         
23836     },
23837
23838     // private
23839     onResize : function(w, h)
23840     {
23841         Roo.log('resize: ' +w + ',' + h );
23842         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23843         var ew = false;
23844         var eh = false;
23845         
23846         if(this.inputEl() ){
23847             if(typeof w == 'number'){
23848                 var aw = w - this.wrap.getFrameWidth('lr');
23849                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23850                 ew = aw;
23851             }
23852             if(typeof h == 'number'){
23853                  var tbh = -11;  // fixme it needs to tool bar size!
23854                 for (var i =0; i < this.toolbars.length;i++) {
23855                     // fixme - ask toolbars for heights?
23856                     tbh += this.toolbars[i].el.getHeight();
23857                     //if (this.toolbars[i].footer) {
23858                     //    tbh += this.toolbars[i].footer.el.getHeight();
23859                     //}
23860                 }
23861               
23862                 
23863                 
23864                 
23865                 
23866                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23867                 ah -= 5; // knock a few pixes off for look..
23868                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23869                 var eh = ah;
23870             }
23871         }
23872         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23873         this.editorcore.onResize(ew,eh);
23874         
23875     },
23876
23877     /**
23878      * Toggles the editor between standard and source edit mode.
23879      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23880      */
23881     toggleSourceEdit : function(sourceEditMode)
23882     {
23883         this.editorcore.toggleSourceEdit(sourceEditMode);
23884         
23885         if(this.editorcore.sourceEditMode){
23886             Roo.log('editor - showing textarea');
23887             
23888 //            Roo.log('in');
23889 //            Roo.log(this.syncValue());
23890             this.syncValue();
23891             this.inputEl().removeClass(['hide', 'x-hidden']);
23892             this.inputEl().dom.removeAttribute('tabIndex');
23893             this.inputEl().focus();
23894         }else{
23895             Roo.log('editor - hiding textarea');
23896 //            Roo.log('out')
23897 //            Roo.log(this.pushValue()); 
23898             this.pushValue();
23899             
23900             this.inputEl().addClass(['hide', 'x-hidden']);
23901             this.inputEl().dom.setAttribute('tabIndex', -1);
23902             //this.deferFocus();
23903         }
23904          
23905         if(this.resizable){
23906             this.setSize(this.wrap.getSize());
23907         }
23908         
23909         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23910     },
23911  
23912     // private (for BoxComponent)
23913     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23914
23915     // private (for BoxComponent)
23916     getResizeEl : function(){
23917         return this.wrap;
23918     },
23919
23920     // private (for BoxComponent)
23921     getPositionEl : function(){
23922         return this.wrap;
23923     },
23924
23925     // private
23926     initEvents : function(){
23927         this.originalValue = this.getValue();
23928     },
23929
23930 //    /**
23931 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23932 //     * @method
23933 //     */
23934 //    markInvalid : Roo.emptyFn,
23935 //    /**
23936 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23937 //     * @method
23938 //     */
23939 //    clearInvalid : Roo.emptyFn,
23940
23941     setValue : function(v){
23942         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23943         this.editorcore.pushValue();
23944     },
23945
23946      
23947     // private
23948     deferFocus : function(){
23949         this.focus.defer(10, this);
23950     },
23951
23952     // doc'ed in Field
23953     focus : function(){
23954         this.editorcore.focus();
23955         
23956     },
23957       
23958
23959     // private
23960     onDestroy : function(){
23961         
23962         
23963         
23964         if(this.rendered){
23965             
23966             for (var i =0; i < this.toolbars.length;i++) {
23967                 // fixme - ask toolbars for heights?
23968                 this.toolbars[i].onDestroy();
23969             }
23970             
23971             this.wrap.dom.innerHTML = '';
23972             this.wrap.remove();
23973         }
23974     },
23975
23976     // private
23977     onFirstFocus : function(){
23978         //Roo.log("onFirstFocus");
23979         this.editorcore.onFirstFocus();
23980          for (var i =0; i < this.toolbars.length;i++) {
23981             this.toolbars[i].onFirstFocus();
23982         }
23983         
23984     },
23985     
23986     // private
23987     syncValue : function()
23988     {   
23989         this.editorcore.syncValue();
23990     },
23991     
23992     pushValue : function()
23993     {   
23994         this.editorcore.pushValue();
23995     }
23996      
23997     
23998     // hide stuff that is not compatible
23999     /**
24000      * @event blur
24001      * @hide
24002      */
24003     /**
24004      * @event change
24005      * @hide
24006      */
24007     /**
24008      * @event focus
24009      * @hide
24010      */
24011     /**
24012      * @event specialkey
24013      * @hide
24014      */
24015     /**
24016      * @cfg {String} fieldClass @hide
24017      */
24018     /**
24019      * @cfg {String} focusClass @hide
24020      */
24021     /**
24022      * @cfg {String} autoCreate @hide
24023      */
24024     /**
24025      * @cfg {String} inputType @hide
24026      */
24027      
24028     /**
24029      * @cfg {String} invalidText @hide
24030      */
24031     /**
24032      * @cfg {String} msgFx @hide
24033      */
24034     /**
24035      * @cfg {String} validateOnBlur @hide
24036      */
24037 });
24038  
24039     
24040    
24041    
24042    
24043       
24044 Roo.namespace('Roo.bootstrap.htmleditor');
24045 /**
24046  * @class Roo.bootstrap.HtmlEditorToolbar1
24047  * Basic Toolbar
24048  * 
24049  * Usage:
24050  *
24051  new Roo.bootstrap.HtmlEditor({
24052     ....
24053     toolbars : [
24054         new Roo.bootstrap.HtmlEditorToolbar1({
24055             disable : { fonts: 1 , format: 1, ..., ... , ...],
24056             btns : [ .... ]
24057         })
24058     }
24059      
24060  * 
24061  * @cfg {Object} disable List of elements to disable..
24062  * @cfg {Array} btns List of additional buttons.
24063  * 
24064  * 
24065  * NEEDS Extra CSS? 
24066  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24067  */
24068  
24069 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
24070 {
24071     
24072     Roo.apply(this, config);
24073     
24074     // default disabled, based on 'good practice'..
24075     this.disable = this.disable || {};
24076     Roo.applyIf(this.disable, {
24077         fontSize : true,
24078         colors : true,
24079         specialElements : true
24080     });
24081     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
24082     
24083     this.editor = config.editor;
24084     this.editorcore = config.editor.editorcore;
24085     
24086     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
24087     
24088     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24089     // dont call parent... till later.
24090 }
24091 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
24092      
24093     bar : true,
24094     
24095     editor : false,
24096     editorcore : false,
24097     
24098     
24099     formats : [
24100         "p" ,  
24101         "h1","h2","h3","h4","h5","h6", 
24102         "pre", "code", 
24103         "abbr", "acronym", "address", "cite", "samp", "var",
24104         'div','span'
24105     ],
24106     
24107     onRender : function(ct, position)
24108     {
24109        // Roo.log("Call onRender: " + this.xtype);
24110         
24111        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
24112        Roo.log(this.el);
24113        this.el.dom.style.marginBottom = '0';
24114        var _this = this;
24115        var editorcore = this.editorcore;
24116        var editor= this.editor;
24117        
24118        var children = [];
24119        var btn = function(id,cmd , toggle, handler, html){
24120        
24121             var  event = toggle ? 'toggle' : 'click';
24122        
24123             var a = {
24124                 size : 'sm',
24125                 xtype: 'Button',
24126                 xns: Roo.bootstrap,
24127                 //glyphicon : id,
24128                 fa: id,
24129                 cmd : id || cmd,
24130                 enableToggle:toggle !== false,
24131                 html : html || '',
24132                 pressed : toggle ? false : null,
24133                 listeners : {}
24134             };
24135             a.listeners[toggle ? 'toggle' : 'click'] = function() {
24136                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
24137             };
24138             children.push(a);
24139             return a;
24140        }
24141        
24142     //    var cb_box = function...
24143         
24144         var style = {
24145                 xtype: 'Button',
24146                 size : 'sm',
24147                 xns: Roo.bootstrap,
24148                 fa : 'font',
24149                 //html : 'submit'
24150                 menu : {
24151                     xtype: 'Menu',
24152                     xns: Roo.bootstrap,
24153                     items:  []
24154                 }
24155         };
24156         Roo.each(this.formats, function(f) {
24157             style.menu.items.push({
24158                 xtype :'MenuItem',
24159                 xns: Roo.bootstrap,
24160                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
24161                 tagname : f,
24162                 listeners : {
24163                     click : function()
24164                     {
24165                         editorcore.insertTag(this.tagname);
24166                         editor.focus();
24167                     }
24168                 }
24169                 
24170             });
24171         });
24172         children.push(style);   
24173         
24174         btn('bold',false,true);
24175         btn('italic',false,true);
24176         btn('align-left', 'justifyleft',true);
24177         btn('align-center', 'justifycenter',true);
24178         btn('align-right' , 'justifyright',true);
24179         btn('link', false, false, function(btn) {
24180             //Roo.log("create link?");
24181             var url = prompt(this.createLinkText, this.defaultLinkValue);
24182             if(url && url != 'http:/'+'/'){
24183                 this.editorcore.relayCmd('createlink', url);
24184             }
24185         }),
24186         btn('list','insertunorderedlist',true);
24187         btn('pencil', false,true, function(btn){
24188                 Roo.log(this);
24189                 this.toggleSourceEdit(btn.pressed);
24190         });
24191         
24192         if (this.editor.btns.length > 0) {
24193             for (var i = 0; i<this.editor.btns.length; i++) {
24194                 children.push(this.editor.btns[i]);
24195             }
24196         }
24197         
24198         /*
24199         var cog = {
24200                 xtype: 'Button',
24201                 size : 'sm',
24202                 xns: Roo.bootstrap,
24203                 glyphicon : 'cog',
24204                 //html : 'submit'
24205                 menu : {
24206                     xtype: 'Menu',
24207                     xns: Roo.bootstrap,
24208                     items:  []
24209                 }
24210         };
24211         
24212         cog.menu.items.push({
24213             xtype :'MenuItem',
24214             xns: Roo.bootstrap,
24215             html : Clean styles,
24216             tagname : f,
24217             listeners : {
24218                 click : function()
24219                 {
24220                     editorcore.insertTag(this.tagname);
24221                     editor.focus();
24222                 }
24223             }
24224             
24225         });
24226        */
24227         
24228          
24229        this.xtype = 'NavSimplebar';
24230         
24231         for(var i=0;i< children.length;i++) {
24232             
24233             this.buttons.add(this.addxtypeChild(children[i]));
24234             
24235         }
24236         
24237         editor.on('editorevent', this.updateToolbar, this);
24238     },
24239     onBtnClick : function(id)
24240     {
24241        this.editorcore.relayCmd(id);
24242        this.editorcore.focus();
24243     },
24244     
24245     /**
24246      * Protected method that will not generally be called directly. It triggers
24247      * a toolbar update by reading the markup state of the current selection in the editor.
24248      */
24249     updateToolbar: function(){
24250
24251         if(!this.editorcore.activated){
24252             this.editor.onFirstFocus(); // is this neeed?
24253             return;
24254         }
24255
24256         var btns = this.buttons; 
24257         var doc = this.editorcore.doc;
24258         btns.get('bold').setActive(doc.queryCommandState('bold'));
24259         btns.get('italic').setActive(doc.queryCommandState('italic'));
24260         //btns.get('underline').setActive(doc.queryCommandState('underline'));
24261         
24262         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
24263         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
24264         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
24265         
24266         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
24267         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
24268          /*
24269         
24270         var ans = this.editorcore.getAllAncestors();
24271         if (this.formatCombo) {
24272             
24273             
24274             var store = this.formatCombo.store;
24275             this.formatCombo.setValue("");
24276             for (var i =0; i < ans.length;i++) {
24277                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
24278                     // select it..
24279                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
24280                     break;
24281                 }
24282             }
24283         }
24284         
24285         
24286         
24287         // hides menus... - so this cant be on a menu...
24288         Roo.bootstrap.MenuMgr.hideAll();
24289         */
24290         Roo.bootstrap.MenuMgr.hideAll();
24291         //this.editorsyncValue();
24292     },
24293     onFirstFocus: function() {
24294         this.buttons.each(function(item){
24295            item.enable();
24296         });
24297     },
24298     toggleSourceEdit : function(sourceEditMode){
24299         
24300           
24301         if(sourceEditMode){
24302             Roo.log("disabling buttons");
24303            this.buttons.each( function(item){
24304                 if(item.cmd != 'pencil'){
24305                     item.disable();
24306                 }
24307             });
24308           
24309         }else{
24310             Roo.log("enabling buttons");
24311             if(this.editorcore.initialized){
24312                 this.buttons.each( function(item){
24313                     item.enable();
24314                 });
24315             }
24316             
24317         }
24318         Roo.log("calling toggole on editor");
24319         // tell the editor that it's been pressed..
24320         this.editor.toggleSourceEdit(sourceEditMode);
24321        
24322     }
24323 });
24324
24325
24326
24327
24328
24329 /**
24330  * @class Roo.bootstrap.Table.AbstractSelectionModel
24331  * @extends Roo.util.Observable
24332  * Abstract base class for grid SelectionModels.  It provides the interface that should be
24333  * implemented by descendant classes.  This class should not be directly instantiated.
24334  * @constructor
24335  */
24336 Roo.bootstrap.Table.AbstractSelectionModel = function(){
24337     this.locked = false;
24338     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
24339 };
24340
24341
24342 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
24343     /** @ignore Called by the grid automatically. Do not call directly. */
24344     init : function(grid){
24345         this.grid = grid;
24346         this.initEvents();
24347     },
24348
24349     /**
24350      * Locks the selections.
24351      */
24352     lock : function(){
24353         this.locked = true;
24354     },
24355
24356     /**
24357      * Unlocks the selections.
24358      */
24359     unlock : function(){
24360         this.locked = false;
24361     },
24362
24363     /**
24364      * Returns true if the selections are locked.
24365      * @return {Boolean}
24366      */
24367     isLocked : function(){
24368         return this.locked;
24369     }
24370 });
24371 /**
24372  * @extends Roo.bootstrap.Table.AbstractSelectionModel
24373  * @class Roo.bootstrap.Table.RowSelectionModel
24374  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
24375  * It supports multiple selections and keyboard selection/navigation. 
24376  * @constructor
24377  * @param {Object} config
24378  */
24379
24380 Roo.bootstrap.Table.RowSelectionModel = function(config){
24381     Roo.apply(this, config);
24382     this.selections = new Roo.util.MixedCollection(false, function(o){
24383         return o.id;
24384     });
24385
24386     this.last = false;
24387     this.lastActive = false;
24388
24389     this.addEvents({
24390         /**
24391              * @event selectionchange
24392              * Fires when the selection changes
24393              * @param {SelectionModel} this
24394              */
24395             "selectionchange" : true,
24396         /**
24397              * @event afterselectionchange
24398              * Fires after the selection changes (eg. by key press or clicking)
24399              * @param {SelectionModel} this
24400              */
24401             "afterselectionchange" : true,
24402         /**
24403              * @event beforerowselect
24404              * Fires when a row is selected being selected, return false to cancel.
24405              * @param {SelectionModel} this
24406              * @param {Number} rowIndex The selected index
24407              * @param {Boolean} keepExisting False if other selections will be cleared
24408              */
24409             "beforerowselect" : true,
24410         /**
24411              * @event rowselect
24412              * Fires when a row is selected.
24413              * @param {SelectionModel} this
24414              * @param {Number} rowIndex The selected index
24415              * @param {Roo.data.Record} r The record
24416              */
24417             "rowselect" : true,
24418         /**
24419              * @event rowdeselect
24420              * Fires when a row is deselected.
24421              * @param {SelectionModel} this
24422              * @param {Number} rowIndex The selected index
24423              */
24424         "rowdeselect" : true
24425     });
24426     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
24427     this.locked = false;
24428  };
24429
24430 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
24431     /**
24432      * @cfg {Boolean} singleSelect
24433      * True to allow selection of only one row at a time (defaults to false)
24434      */
24435     singleSelect : false,
24436
24437     // private
24438     initEvents : function()
24439     {
24440
24441         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
24442         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
24443         //}else{ // allow click to work like normal
24444          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
24445         //}
24446         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
24447         this.grid.on("rowclick", this.handleMouseDown, this);
24448         
24449         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
24450             "up" : function(e){
24451                 if(!e.shiftKey){
24452                     this.selectPrevious(e.shiftKey);
24453                 }else if(this.last !== false && this.lastActive !== false){
24454                     var last = this.last;
24455                     this.selectRange(this.last,  this.lastActive-1);
24456                     this.grid.getView().focusRow(this.lastActive);
24457                     if(last !== false){
24458                         this.last = last;
24459                     }
24460                 }else{
24461                     this.selectFirstRow();
24462                 }
24463                 this.fireEvent("afterselectionchange", this);
24464             },
24465             "down" : function(e){
24466                 if(!e.shiftKey){
24467                     this.selectNext(e.shiftKey);
24468                 }else if(this.last !== false && this.lastActive !== false){
24469                     var last = this.last;
24470                     this.selectRange(this.last,  this.lastActive+1);
24471                     this.grid.getView().focusRow(this.lastActive);
24472                     if(last !== false){
24473                         this.last = last;
24474                     }
24475                 }else{
24476                     this.selectFirstRow();
24477                 }
24478                 this.fireEvent("afterselectionchange", this);
24479             },
24480             scope: this
24481         });
24482         this.grid.store.on('load', function(){
24483             this.selections.clear();
24484         },this);
24485         /*
24486         var view = this.grid.view;
24487         view.on("refresh", this.onRefresh, this);
24488         view.on("rowupdated", this.onRowUpdated, this);
24489         view.on("rowremoved", this.onRemove, this);
24490         */
24491     },
24492
24493     // private
24494     onRefresh : function()
24495     {
24496         var ds = this.grid.store, i, v = this.grid.view;
24497         var s = this.selections;
24498         s.each(function(r){
24499             if((i = ds.indexOfId(r.id)) != -1){
24500                 v.onRowSelect(i);
24501             }else{
24502                 s.remove(r);
24503             }
24504         });
24505     },
24506
24507     // private
24508     onRemove : function(v, index, r){
24509         this.selections.remove(r);
24510     },
24511
24512     // private
24513     onRowUpdated : function(v, index, r){
24514         if(this.isSelected(r)){
24515             v.onRowSelect(index);
24516         }
24517     },
24518
24519     /**
24520      * Select records.
24521      * @param {Array} records The records to select
24522      * @param {Boolean} keepExisting (optional) True to keep existing selections
24523      */
24524     selectRecords : function(records, keepExisting)
24525     {
24526         if(!keepExisting){
24527             this.clearSelections();
24528         }
24529             var ds = this.grid.store;
24530         for(var i = 0, len = records.length; i < len; i++){
24531             this.selectRow(ds.indexOf(records[i]), true);
24532         }
24533     },
24534
24535     /**
24536      * Gets the number of selected rows.
24537      * @return {Number}
24538      */
24539     getCount : function(){
24540         return this.selections.length;
24541     },
24542
24543     /**
24544      * Selects the first row in the grid.
24545      */
24546     selectFirstRow : function(){
24547         this.selectRow(0);
24548     },
24549
24550     /**
24551      * Select the last row.
24552      * @param {Boolean} keepExisting (optional) True to keep existing selections
24553      */
24554     selectLastRow : function(keepExisting){
24555         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
24556         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
24557     },
24558
24559     /**
24560      * Selects the row immediately following the last selected row.
24561      * @param {Boolean} keepExisting (optional) True to keep existing selections
24562      */
24563     selectNext : function(keepExisting)
24564     {
24565             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
24566             this.selectRow(this.last+1, keepExisting);
24567             this.grid.getView().focusRow(this.last);
24568         }
24569     },
24570
24571     /**
24572      * Selects the row that precedes the last selected row.
24573      * @param {Boolean} keepExisting (optional) True to keep existing selections
24574      */
24575     selectPrevious : function(keepExisting){
24576         if(this.last){
24577             this.selectRow(this.last-1, keepExisting);
24578             this.grid.getView().focusRow(this.last);
24579         }
24580     },
24581
24582     /**
24583      * Returns the selected records
24584      * @return {Array} Array of selected records
24585      */
24586     getSelections : function(){
24587         return [].concat(this.selections.items);
24588     },
24589
24590     /**
24591      * Returns the first selected record.
24592      * @return {Record}
24593      */
24594     getSelected : function(){
24595         return this.selections.itemAt(0);
24596     },
24597
24598
24599     /**
24600      * Clears all selections.
24601      */
24602     clearSelections : function(fast)
24603     {
24604         if(this.locked) {
24605             return;
24606         }
24607         if(fast !== true){
24608                 var ds = this.grid.store;
24609             var s = this.selections;
24610             s.each(function(r){
24611                 this.deselectRow(ds.indexOfId(r.id));
24612             }, this);
24613             s.clear();
24614         }else{
24615             this.selections.clear();
24616         }
24617         this.last = false;
24618     },
24619
24620
24621     /**
24622      * Selects all rows.
24623      */
24624     selectAll : function(){
24625         if(this.locked) {
24626             return;
24627         }
24628         this.selections.clear();
24629         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
24630             this.selectRow(i, true);
24631         }
24632     },
24633
24634     /**
24635      * Returns True if there is a selection.
24636      * @return {Boolean}
24637      */
24638     hasSelection : function(){
24639         return this.selections.length > 0;
24640     },
24641
24642     /**
24643      * Returns True if the specified row is selected.
24644      * @param {Number/Record} record The record or index of the record to check
24645      * @return {Boolean}
24646      */
24647     isSelected : function(index){
24648             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24649         return (r && this.selections.key(r.id) ? true : false);
24650     },
24651
24652     /**
24653      * Returns True if the specified record id is selected.
24654      * @param {String} id The id of record to check
24655      * @return {Boolean}
24656      */
24657     isIdSelected : function(id){
24658         return (this.selections.key(id) ? true : false);
24659     },
24660
24661
24662     // private
24663     handleMouseDBClick : function(e, t){
24664         
24665     },
24666     // private
24667     handleMouseDown : function(e, t)
24668     {
24669             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24670         if(this.isLocked() || rowIndex < 0 ){
24671             return;
24672         };
24673         if(e.shiftKey && this.last !== false){
24674             var last = this.last;
24675             this.selectRange(last, rowIndex, e.ctrlKey);
24676             this.last = last; // reset the last
24677             t.focus();
24678     
24679         }else{
24680             var isSelected = this.isSelected(rowIndex);
24681             //Roo.log("select row:" + rowIndex);
24682             if(isSelected){
24683                 this.deselectRow(rowIndex);
24684             } else {
24685                         this.selectRow(rowIndex, true);
24686             }
24687     
24688             /*
24689                 if(e.button !== 0 && isSelected){
24690                 alert('rowIndex 2: ' + rowIndex);
24691                     view.focusRow(rowIndex);
24692                 }else if(e.ctrlKey && isSelected){
24693                     this.deselectRow(rowIndex);
24694                 }else if(!isSelected){
24695                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24696                     view.focusRow(rowIndex);
24697                 }
24698             */
24699         }
24700         this.fireEvent("afterselectionchange", this);
24701     },
24702     // private
24703     handleDragableRowClick :  function(grid, rowIndex, e) 
24704     {
24705         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24706             this.selectRow(rowIndex, false);
24707             grid.view.focusRow(rowIndex);
24708              this.fireEvent("afterselectionchange", this);
24709         }
24710     },
24711     
24712     /**
24713      * Selects multiple rows.
24714      * @param {Array} rows Array of the indexes of the row to select
24715      * @param {Boolean} keepExisting (optional) True to keep existing selections
24716      */
24717     selectRows : function(rows, keepExisting){
24718         if(!keepExisting){
24719             this.clearSelections();
24720         }
24721         for(var i = 0, len = rows.length; i < len; i++){
24722             this.selectRow(rows[i], true);
24723         }
24724     },
24725
24726     /**
24727      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24728      * @param {Number} startRow The index of the first row in the range
24729      * @param {Number} endRow The index of the last row in the range
24730      * @param {Boolean} keepExisting (optional) True to retain existing selections
24731      */
24732     selectRange : function(startRow, endRow, keepExisting){
24733         if(this.locked) {
24734             return;
24735         }
24736         if(!keepExisting){
24737             this.clearSelections();
24738         }
24739         if(startRow <= endRow){
24740             for(var i = startRow; i <= endRow; i++){
24741                 this.selectRow(i, true);
24742             }
24743         }else{
24744             for(var i = startRow; i >= endRow; i--){
24745                 this.selectRow(i, true);
24746             }
24747         }
24748     },
24749
24750     /**
24751      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24752      * @param {Number} startRow The index of the first row in the range
24753      * @param {Number} endRow The index of the last row in the range
24754      */
24755     deselectRange : function(startRow, endRow, preventViewNotify){
24756         if(this.locked) {
24757             return;
24758         }
24759         for(var i = startRow; i <= endRow; i++){
24760             this.deselectRow(i, preventViewNotify);
24761         }
24762     },
24763
24764     /**
24765      * Selects a row.
24766      * @param {Number} row The index of the row to select
24767      * @param {Boolean} keepExisting (optional) True to keep existing selections
24768      */
24769     selectRow : function(index, keepExisting, preventViewNotify)
24770     {
24771             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24772             return;
24773         }
24774         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24775             if(!keepExisting || this.singleSelect){
24776                 this.clearSelections();
24777             }
24778             
24779             var r = this.grid.store.getAt(index);
24780             //console.log('selectRow - record id :' + r.id);
24781             
24782             this.selections.add(r);
24783             this.last = this.lastActive = index;
24784             if(!preventViewNotify){
24785                 var proxy = new Roo.Element(
24786                                 this.grid.getRowDom(index)
24787                 );
24788                 proxy.addClass('bg-info info');
24789             }
24790             this.fireEvent("rowselect", this, index, r);
24791             this.fireEvent("selectionchange", this);
24792         }
24793     },
24794
24795     /**
24796      * Deselects a row.
24797      * @param {Number} row The index of the row to deselect
24798      */
24799     deselectRow : function(index, preventViewNotify)
24800     {
24801         if(this.locked) {
24802             return;
24803         }
24804         if(this.last == index){
24805             this.last = false;
24806         }
24807         if(this.lastActive == index){
24808             this.lastActive = false;
24809         }
24810         
24811         var r = this.grid.store.getAt(index);
24812         if (!r) {
24813             return;
24814         }
24815         
24816         this.selections.remove(r);
24817         //.console.log('deselectRow - record id :' + r.id);
24818         if(!preventViewNotify){
24819         
24820             var proxy = new Roo.Element(
24821                 this.grid.getRowDom(index)
24822             );
24823             proxy.removeClass('bg-info info');
24824         }
24825         this.fireEvent("rowdeselect", this, index);
24826         this.fireEvent("selectionchange", this);
24827     },
24828
24829     // private
24830     restoreLast : function(){
24831         if(this._last){
24832             this.last = this._last;
24833         }
24834     },
24835
24836     // private
24837     acceptsNav : function(row, col, cm){
24838         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24839     },
24840
24841     // private
24842     onEditorKey : function(field, e){
24843         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24844         if(k == e.TAB){
24845             e.stopEvent();
24846             ed.completeEdit();
24847             if(e.shiftKey){
24848                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24849             }else{
24850                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24851             }
24852         }else if(k == e.ENTER && !e.ctrlKey){
24853             e.stopEvent();
24854             ed.completeEdit();
24855             if(e.shiftKey){
24856                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24857             }else{
24858                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24859             }
24860         }else if(k == e.ESC){
24861             ed.cancelEdit();
24862         }
24863         if(newCell){
24864             g.startEditing(newCell[0], newCell[1]);
24865         }
24866     }
24867 });
24868 /*
24869  * Based on:
24870  * Ext JS Library 1.1.1
24871  * Copyright(c) 2006-2007, Ext JS, LLC.
24872  *
24873  * Originally Released Under LGPL - original licence link has changed is not relivant.
24874  *
24875  * Fork - LGPL
24876  * <script type="text/javascript">
24877  */
24878  
24879 /**
24880  * @class Roo.bootstrap.PagingToolbar
24881  * @extends Roo.bootstrap.NavSimplebar
24882  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24883  * @constructor
24884  * Create a new PagingToolbar
24885  * @param {Object} config The config object
24886  * @param {Roo.data.Store} store
24887  */
24888 Roo.bootstrap.PagingToolbar = function(config)
24889 {
24890     // old args format still supported... - xtype is prefered..
24891         // created from xtype...
24892     
24893     this.ds = config.dataSource;
24894     
24895     if (config.store && !this.ds) {
24896         this.store= Roo.factory(config.store, Roo.data);
24897         this.ds = this.store;
24898         this.ds.xmodule = this.xmodule || false;
24899     }
24900     
24901     this.toolbarItems = [];
24902     if (config.items) {
24903         this.toolbarItems = config.items;
24904     }
24905     
24906     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24907     
24908     this.cursor = 0;
24909     
24910     if (this.ds) { 
24911         this.bind(this.ds);
24912     }
24913     
24914     if (Roo.bootstrap.version == 4) {
24915         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
24916     } else {
24917         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24918     }
24919     
24920 };
24921
24922 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24923     /**
24924      * @cfg {Roo.data.Store} dataSource
24925      * The underlying data store providing the paged data
24926      */
24927     /**
24928      * @cfg {String/HTMLElement/Element} container
24929      * container The id or element that will contain the toolbar
24930      */
24931     /**
24932      * @cfg {Boolean} displayInfo
24933      * True to display the displayMsg (defaults to false)
24934      */
24935     /**
24936      * @cfg {Number} pageSize
24937      * The number of records to display per page (defaults to 20)
24938      */
24939     pageSize: 20,
24940     /**
24941      * @cfg {String} displayMsg
24942      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24943      */
24944     displayMsg : 'Displaying {0} - {1} of {2}',
24945     /**
24946      * @cfg {String} emptyMsg
24947      * The message to display when no records are found (defaults to "No data to display")
24948      */
24949     emptyMsg : 'No data to display',
24950     /**
24951      * Customizable piece of the default paging text (defaults to "Page")
24952      * @type String
24953      */
24954     beforePageText : "Page",
24955     /**
24956      * Customizable piece of the default paging text (defaults to "of %0")
24957      * @type String
24958      */
24959     afterPageText : "of {0}",
24960     /**
24961      * Customizable piece of the default paging text (defaults to "First Page")
24962      * @type String
24963      */
24964     firstText : "First Page",
24965     /**
24966      * Customizable piece of the default paging text (defaults to "Previous Page")
24967      * @type String
24968      */
24969     prevText : "Previous Page",
24970     /**
24971      * Customizable piece of the default paging text (defaults to "Next Page")
24972      * @type String
24973      */
24974     nextText : "Next Page",
24975     /**
24976      * Customizable piece of the default paging text (defaults to "Last Page")
24977      * @type String
24978      */
24979     lastText : "Last Page",
24980     /**
24981      * Customizable piece of the default paging text (defaults to "Refresh")
24982      * @type String
24983      */
24984     refreshText : "Refresh",
24985
24986     buttons : false,
24987     // private
24988     onRender : function(ct, position) 
24989     {
24990         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24991         this.navgroup.parentId = this.id;
24992         this.navgroup.onRender(this.el, null);
24993         // add the buttons to the navgroup
24994         
24995         if(this.displayInfo){
24996             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24997             this.displayEl = this.el.select('.x-paging-info', true).first();
24998 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24999 //            this.displayEl = navel.el.select('span',true).first();
25000         }
25001         
25002         var _this = this;
25003         
25004         if(this.buttons){
25005             Roo.each(_this.buttons, function(e){ // this might need to use render????
25006                Roo.factory(e).render(_this.el);
25007             });
25008         }
25009             
25010         Roo.each(_this.toolbarItems, function(e) {
25011             _this.navgroup.addItem(e);
25012         });
25013         
25014         
25015         this.first = this.navgroup.addItem({
25016             tooltip: this.firstText,
25017             cls: "prev btn-outline-secondary",
25018             html : ' <i class="fa fa-step-backward"></i>',
25019             disabled: true,
25020             preventDefault: true,
25021             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
25022         });
25023         
25024         this.prev =  this.navgroup.addItem({
25025             tooltip: this.prevText,
25026             cls: "prev btn-outline-secondary",
25027             html : ' <i class="fa fa-backward"></i>',
25028             disabled: true,
25029             preventDefault: true,
25030             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
25031         });
25032     //this.addSeparator();
25033         
25034         
25035         var field = this.navgroup.addItem( {
25036             tagtype : 'span',
25037             cls : 'x-paging-position  btn-outline-secondary',
25038              disabled: true,
25039             html : this.beforePageText  +
25040                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
25041                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
25042          } ); //?? escaped?
25043         
25044         this.field = field.el.select('input', true).first();
25045         this.field.on("keydown", this.onPagingKeydown, this);
25046         this.field.on("focus", function(){this.dom.select();});
25047     
25048     
25049         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
25050         //this.field.setHeight(18);
25051         //this.addSeparator();
25052         this.next = this.navgroup.addItem({
25053             tooltip: this.nextText,
25054             cls: "next btn-outline-secondary",
25055             html : ' <i class="fa fa-forward"></i>',
25056             disabled: true,
25057             preventDefault: true,
25058             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
25059         });
25060         this.last = this.navgroup.addItem({
25061             tooltip: this.lastText,
25062             html : ' <i class="fa fa-step-forward"></i>',
25063             cls: "next btn-outline-secondary",
25064             disabled: true,
25065             preventDefault: true,
25066             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
25067         });
25068     //this.addSeparator();
25069         this.loading = this.navgroup.addItem({
25070             tooltip: this.refreshText,
25071             cls: "btn-outline-secondary",
25072             html : ' <i class="fa fa-refresh"></i>',
25073             preventDefault: true,
25074             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
25075         });
25076         
25077     },
25078
25079     // private
25080     updateInfo : function(){
25081         if(this.displayEl){
25082             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
25083             var msg = count == 0 ?
25084                 this.emptyMsg :
25085                 String.format(
25086                     this.displayMsg,
25087                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
25088                 );
25089             this.displayEl.update(msg);
25090         }
25091     },
25092
25093     // private
25094     onLoad : function(ds, r, o)
25095     {
25096         this.cursor = o.params.start ? o.params.start : 0;
25097         
25098         var d = this.getPageData(),
25099             ap = d.activePage,
25100             ps = d.pages;
25101         
25102         
25103         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
25104         this.field.dom.value = ap;
25105         this.first.setDisabled(ap == 1);
25106         this.prev.setDisabled(ap == 1);
25107         this.next.setDisabled(ap == ps);
25108         this.last.setDisabled(ap == ps);
25109         this.loading.enable();
25110         this.updateInfo();
25111     },
25112
25113     // private
25114     getPageData : function(){
25115         var total = this.ds.getTotalCount();
25116         return {
25117             total : total,
25118             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
25119             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
25120         };
25121     },
25122
25123     // private
25124     onLoadError : function(){
25125         this.loading.enable();
25126     },
25127
25128     // private
25129     onPagingKeydown : function(e){
25130         var k = e.getKey();
25131         var d = this.getPageData();
25132         if(k == e.RETURN){
25133             var v = this.field.dom.value, pageNum;
25134             if(!v || isNaN(pageNum = parseInt(v, 10))){
25135                 this.field.dom.value = d.activePage;
25136                 return;
25137             }
25138             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
25139             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25140             e.stopEvent();
25141         }
25142         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))
25143         {
25144           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
25145           this.field.dom.value = pageNum;
25146           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
25147           e.stopEvent();
25148         }
25149         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
25150         {
25151           var v = this.field.dom.value, pageNum; 
25152           var increment = (e.shiftKey) ? 10 : 1;
25153           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
25154                 increment *= -1;
25155           }
25156           if(!v || isNaN(pageNum = parseInt(v, 10))) {
25157             this.field.dom.value = d.activePage;
25158             return;
25159           }
25160           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
25161           {
25162             this.field.dom.value = parseInt(v, 10) + increment;
25163             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
25164             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
25165           }
25166           e.stopEvent();
25167         }
25168     },
25169
25170     // private
25171     beforeLoad : function(){
25172         if(this.loading){
25173             this.loading.disable();
25174         }
25175     },
25176
25177     // private
25178     onClick : function(which){
25179         
25180         var ds = this.ds;
25181         if (!ds) {
25182             return;
25183         }
25184         
25185         switch(which){
25186             case "first":
25187                 ds.load({params:{start: 0, limit: this.pageSize}});
25188             break;
25189             case "prev":
25190                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
25191             break;
25192             case "next":
25193                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
25194             break;
25195             case "last":
25196                 var total = ds.getTotalCount();
25197                 var extra = total % this.pageSize;
25198                 var lastStart = extra ? (total - extra) : total-this.pageSize;
25199                 ds.load({params:{start: lastStart, limit: this.pageSize}});
25200             break;
25201             case "refresh":
25202                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
25203             break;
25204         }
25205     },
25206
25207     /**
25208      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
25209      * @param {Roo.data.Store} store The data store to unbind
25210      */
25211     unbind : function(ds){
25212         ds.un("beforeload", this.beforeLoad, this);
25213         ds.un("load", this.onLoad, this);
25214         ds.un("loadexception", this.onLoadError, this);
25215         ds.un("remove", this.updateInfo, this);
25216         ds.un("add", this.updateInfo, this);
25217         this.ds = undefined;
25218     },
25219
25220     /**
25221      * Binds the paging toolbar to the specified {@link Roo.data.Store}
25222      * @param {Roo.data.Store} store The data store to bind
25223      */
25224     bind : function(ds){
25225         ds.on("beforeload", this.beforeLoad, this);
25226         ds.on("load", this.onLoad, this);
25227         ds.on("loadexception", this.onLoadError, this);
25228         ds.on("remove", this.updateInfo, this);
25229         ds.on("add", this.updateInfo, this);
25230         this.ds = ds;
25231     }
25232 });/*
25233  * - LGPL
25234  *
25235  * element
25236  * 
25237  */
25238
25239 /**
25240  * @class Roo.bootstrap.MessageBar
25241  * @extends Roo.bootstrap.Component
25242  * Bootstrap MessageBar class
25243  * @cfg {String} html contents of the MessageBar
25244  * @cfg {String} weight (info | success | warning | danger) default info
25245  * @cfg {String} beforeClass insert the bar before the given class
25246  * @cfg {Boolean} closable (true | false) default false
25247  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
25248  * 
25249  * @constructor
25250  * Create a new Element
25251  * @param {Object} config The config object
25252  */
25253
25254 Roo.bootstrap.MessageBar = function(config){
25255     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
25256 };
25257
25258 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
25259     
25260     html: '',
25261     weight: 'info',
25262     closable: false,
25263     fixed: false,
25264     beforeClass: 'bootstrap-sticky-wrap',
25265     
25266     getAutoCreate : function(){
25267         
25268         var cfg = {
25269             tag: 'div',
25270             cls: 'alert alert-dismissable alert-' + this.weight,
25271             cn: [
25272                 {
25273                     tag: 'span',
25274                     cls: 'message',
25275                     html: this.html || ''
25276                 }
25277             ]
25278         };
25279         
25280         if(this.fixed){
25281             cfg.cls += ' alert-messages-fixed';
25282         }
25283         
25284         if(this.closable){
25285             cfg.cn.push({
25286                 tag: 'button',
25287                 cls: 'close',
25288                 html: 'x'
25289             });
25290         }
25291         
25292         return cfg;
25293     },
25294     
25295     onRender : function(ct, position)
25296     {
25297         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
25298         
25299         if(!this.el){
25300             var cfg = Roo.apply({},  this.getAutoCreate());
25301             cfg.id = Roo.id();
25302             
25303             if (this.cls) {
25304                 cfg.cls += ' ' + this.cls;
25305             }
25306             if (this.style) {
25307                 cfg.style = this.style;
25308             }
25309             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
25310             
25311             this.el.setVisibilityMode(Roo.Element.DISPLAY);
25312         }
25313         
25314         this.el.select('>button.close').on('click', this.hide, this);
25315         
25316     },
25317     
25318     show : function()
25319     {
25320         if (!this.rendered) {
25321             this.render();
25322         }
25323         
25324         this.el.show();
25325         
25326         this.fireEvent('show', this);
25327         
25328     },
25329     
25330     hide : function()
25331     {
25332         if (!this.rendered) {
25333             this.render();
25334         }
25335         
25336         this.el.hide();
25337         
25338         this.fireEvent('hide', this);
25339     },
25340     
25341     update : function()
25342     {
25343 //        var e = this.el.dom.firstChild;
25344 //        
25345 //        if(this.closable){
25346 //            e = e.nextSibling;
25347 //        }
25348 //        
25349 //        e.data = this.html || '';
25350
25351         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
25352     }
25353    
25354 });
25355
25356  
25357
25358      /*
25359  * - LGPL
25360  *
25361  * Graph
25362  * 
25363  */
25364
25365
25366 /**
25367  * @class Roo.bootstrap.Graph
25368  * @extends Roo.bootstrap.Component
25369  * Bootstrap Graph class
25370 > Prameters
25371  -sm {number} sm 4
25372  -md {number} md 5
25373  @cfg {String} graphtype  bar | vbar | pie
25374  @cfg {number} g_x coodinator | centre x (pie)
25375  @cfg {number} g_y coodinator | centre y (pie)
25376  @cfg {number} g_r radius (pie)
25377  @cfg {number} g_height height of the chart (respected by all elements in the set)
25378  @cfg {number} g_width width of the chart (respected by all elements in the set)
25379  @cfg {Object} title The title of the chart
25380     
25381  -{Array}  values
25382  -opts (object) options for the chart 
25383      o {
25384      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
25385      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
25386      o vgutter (number)
25387      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.
25388      o stacked (boolean) whether or not to tread values as in a stacked bar chart
25389      o to
25390      o stretch (boolean)
25391      o }
25392  -opts (object) options for the pie
25393      o{
25394      o cut
25395      o startAngle (number)
25396      o endAngle (number)
25397      } 
25398  *
25399  * @constructor
25400  * Create a new Input
25401  * @param {Object} config The config object
25402  */
25403
25404 Roo.bootstrap.Graph = function(config){
25405     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
25406     
25407     this.addEvents({
25408         // img events
25409         /**
25410          * @event click
25411          * The img click event for the img.
25412          * @param {Roo.EventObject} e
25413          */
25414         "click" : true
25415     });
25416 };
25417
25418 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
25419     
25420     sm: 4,
25421     md: 5,
25422     graphtype: 'bar',
25423     g_height: 250,
25424     g_width: 400,
25425     g_x: 50,
25426     g_y: 50,
25427     g_r: 30,
25428     opts:{
25429         //g_colors: this.colors,
25430         g_type: 'soft',
25431         g_gutter: '20%'
25432
25433     },
25434     title : false,
25435
25436     getAutoCreate : function(){
25437         
25438         var cfg = {
25439             tag: 'div',
25440             html : null
25441         };
25442         
25443         
25444         return  cfg;
25445     },
25446
25447     onRender : function(ct,position){
25448         
25449         
25450         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
25451         
25452         if (typeof(Raphael) == 'undefined') {
25453             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
25454             return;
25455         }
25456         
25457         this.raphael = Raphael(this.el.dom);
25458         
25459                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25460                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25461                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
25462                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
25463                 /*
25464                 r.text(160, 10, "Single Series Chart").attr(txtattr);
25465                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
25466                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
25467                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
25468                 
25469                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
25470                 r.barchart(330, 10, 300, 220, data1);
25471                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
25472                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
25473                 */
25474                 
25475                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25476                 // r.barchart(30, 30, 560, 250,  xdata, {
25477                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
25478                 //     axis : "0 0 1 1",
25479                 //     axisxlabels :  xdata
25480                 //     //yvalues : cols,
25481                    
25482                 // });
25483 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
25484 //        
25485 //        this.load(null,xdata,{
25486 //                axis : "0 0 1 1",
25487 //                axisxlabels :  xdata
25488 //                });
25489
25490     },
25491
25492     load : function(graphtype,xdata,opts)
25493     {
25494         this.raphael.clear();
25495         if(!graphtype) {
25496             graphtype = this.graphtype;
25497         }
25498         if(!opts){
25499             opts = this.opts;
25500         }
25501         var r = this.raphael,
25502             fin = function () {
25503                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
25504             },
25505             fout = function () {
25506                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
25507             },
25508             pfin = function() {
25509                 this.sector.stop();
25510                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
25511
25512                 if (this.label) {
25513                     this.label[0].stop();
25514                     this.label[0].attr({ r: 7.5 });
25515                     this.label[1].attr({ "font-weight": 800 });
25516                 }
25517             },
25518             pfout = function() {
25519                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
25520
25521                 if (this.label) {
25522                     this.label[0].animate({ r: 5 }, 500, "bounce");
25523                     this.label[1].attr({ "font-weight": 400 });
25524                 }
25525             };
25526
25527         switch(graphtype){
25528             case 'bar':
25529                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25530                 break;
25531             case 'hbar':
25532                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
25533                 break;
25534             case 'pie':
25535 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
25536 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
25537 //            
25538                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
25539                 
25540                 break;
25541
25542         }
25543         
25544         if(this.title){
25545             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
25546         }
25547         
25548     },
25549     
25550     setTitle: function(o)
25551     {
25552         this.title = o;
25553     },
25554     
25555     initEvents: function() {
25556         
25557         if(!this.href){
25558             this.el.on('click', this.onClick, this);
25559         }
25560     },
25561     
25562     onClick : function(e)
25563     {
25564         Roo.log('img onclick');
25565         this.fireEvent('click', this, e);
25566     }
25567    
25568 });
25569
25570  
25571 /*
25572  * - LGPL
25573  *
25574  * numberBox
25575  * 
25576  */
25577 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25578
25579 /**
25580  * @class Roo.bootstrap.dash.NumberBox
25581  * @extends Roo.bootstrap.Component
25582  * Bootstrap NumberBox class
25583  * @cfg {String} headline Box headline
25584  * @cfg {String} content Box content
25585  * @cfg {String} icon Box icon
25586  * @cfg {String} footer Footer text
25587  * @cfg {String} fhref Footer href
25588  * 
25589  * @constructor
25590  * Create a new NumberBox
25591  * @param {Object} config The config object
25592  */
25593
25594
25595 Roo.bootstrap.dash.NumberBox = function(config){
25596     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
25597     
25598 };
25599
25600 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
25601     
25602     headline : '',
25603     content : '',
25604     icon : '',
25605     footer : '',
25606     fhref : '',
25607     ficon : '',
25608     
25609     getAutoCreate : function(){
25610         
25611         var cfg = {
25612             tag : 'div',
25613             cls : 'small-box ',
25614             cn : [
25615                 {
25616                     tag : 'div',
25617                     cls : 'inner',
25618                     cn :[
25619                         {
25620                             tag : 'h3',
25621                             cls : 'roo-headline',
25622                             html : this.headline
25623                         },
25624                         {
25625                             tag : 'p',
25626                             cls : 'roo-content',
25627                             html : this.content
25628                         }
25629                     ]
25630                 }
25631             ]
25632         };
25633         
25634         if(this.icon){
25635             cfg.cn.push({
25636                 tag : 'div',
25637                 cls : 'icon',
25638                 cn :[
25639                     {
25640                         tag : 'i',
25641                         cls : 'ion ' + this.icon
25642                     }
25643                 ]
25644             });
25645         }
25646         
25647         if(this.footer){
25648             var footer = {
25649                 tag : 'a',
25650                 cls : 'small-box-footer',
25651                 href : this.fhref || '#',
25652                 html : this.footer
25653             };
25654             
25655             cfg.cn.push(footer);
25656             
25657         }
25658         
25659         return  cfg;
25660     },
25661
25662     onRender : function(ct,position){
25663         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25664
25665
25666        
25667                 
25668     },
25669
25670     setHeadline: function (value)
25671     {
25672         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25673     },
25674     
25675     setFooter: function (value, href)
25676     {
25677         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25678         
25679         if(href){
25680             this.el.select('a.small-box-footer',true).first().attr('href', href);
25681         }
25682         
25683     },
25684
25685     setContent: function (value)
25686     {
25687         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25688     },
25689
25690     initEvents: function() 
25691     {   
25692         
25693     }
25694     
25695 });
25696
25697  
25698 /*
25699  * - LGPL
25700  *
25701  * TabBox
25702  * 
25703  */
25704 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25705
25706 /**
25707  * @class Roo.bootstrap.dash.TabBox
25708  * @extends Roo.bootstrap.Component
25709  * Bootstrap TabBox class
25710  * @cfg {String} title Title of the TabBox
25711  * @cfg {String} icon Icon of the TabBox
25712  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25713  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25714  * 
25715  * @constructor
25716  * Create a new TabBox
25717  * @param {Object} config The config object
25718  */
25719
25720
25721 Roo.bootstrap.dash.TabBox = function(config){
25722     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25723     this.addEvents({
25724         // raw events
25725         /**
25726          * @event addpane
25727          * When a pane is added
25728          * @param {Roo.bootstrap.dash.TabPane} pane
25729          */
25730         "addpane" : true,
25731         /**
25732          * @event activatepane
25733          * When a pane is activated
25734          * @param {Roo.bootstrap.dash.TabPane} pane
25735          */
25736         "activatepane" : true
25737         
25738          
25739     });
25740     
25741     this.panes = [];
25742 };
25743
25744 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25745
25746     title : '',
25747     icon : false,
25748     showtabs : true,
25749     tabScrollable : false,
25750     
25751     getChildContainer : function()
25752     {
25753         return this.el.select('.tab-content', true).first();
25754     },
25755     
25756     getAutoCreate : function(){
25757         
25758         var header = {
25759             tag: 'li',
25760             cls: 'pull-left header',
25761             html: this.title,
25762             cn : []
25763         };
25764         
25765         if(this.icon){
25766             header.cn.push({
25767                 tag: 'i',
25768                 cls: 'fa ' + this.icon
25769             });
25770         }
25771         
25772         var h = {
25773             tag: 'ul',
25774             cls: 'nav nav-tabs pull-right',
25775             cn: [
25776                 header
25777             ]
25778         };
25779         
25780         if(this.tabScrollable){
25781             h = {
25782                 tag: 'div',
25783                 cls: 'tab-header',
25784                 cn: [
25785                     {
25786                         tag: 'ul',
25787                         cls: 'nav nav-tabs pull-right',
25788                         cn: [
25789                             header
25790                         ]
25791                     }
25792                 ]
25793             };
25794         }
25795         
25796         var cfg = {
25797             tag: 'div',
25798             cls: 'nav-tabs-custom',
25799             cn: [
25800                 h,
25801                 {
25802                     tag: 'div',
25803                     cls: 'tab-content no-padding',
25804                     cn: []
25805                 }
25806             ]
25807         };
25808
25809         return  cfg;
25810     },
25811     initEvents : function()
25812     {
25813         //Roo.log('add add pane handler');
25814         this.on('addpane', this.onAddPane, this);
25815     },
25816      /**
25817      * Updates the box title
25818      * @param {String} html to set the title to.
25819      */
25820     setTitle : function(value)
25821     {
25822         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25823     },
25824     onAddPane : function(pane)
25825     {
25826         this.panes.push(pane);
25827         //Roo.log('addpane');
25828         //Roo.log(pane);
25829         // tabs are rendere left to right..
25830         if(!this.showtabs){
25831             return;
25832         }
25833         
25834         var ctr = this.el.select('.nav-tabs', true).first();
25835          
25836          
25837         var existing = ctr.select('.nav-tab',true);
25838         var qty = existing.getCount();;
25839         
25840         
25841         var tab = ctr.createChild({
25842             tag : 'li',
25843             cls : 'nav-tab' + (qty ? '' : ' active'),
25844             cn : [
25845                 {
25846                     tag : 'a',
25847                     href:'#',
25848                     html : pane.title
25849                 }
25850             ]
25851         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25852         pane.tab = tab;
25853         
25854         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25855         if (!qty) {
25856             pane.el.addClass('active');
25857         }
25858         
25859                 
25860     },
25861     onTabClick : function(ev,un,ob,pane)
25862     {
25863         //Roo.log('tab - prev default');
25864         ev.preventDefault();
25865         
25866         
25867         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25868         pane.tab.addClass('active');
25869         //Roo.log(pane.title);
25870         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25871         // technically we should have a deactivate event.. but maybe add later.
25872         // and it should not de-activate the selected tab...
25873         this.fireEvent('activatepane', pane);
25874         pane.el.addClass('active');
25875         pane.fireEvent('activate');
25876         
25877         
25878     },
25879     
25880     getActivePane : function()
25881     {
25882         var r = false;
25883         Roo.each(this.panes, function(p) {
25884             if(p.el.hasClass('active')){
25885                 r = p;
25886                 return false;
25887             }
25888             
25889             return;
25890         });
25891         
25892         return r;
25893     }
25894     
25895     
25896 });
25897
25898  
25899 /*
25900  * - LGPL
25901  *
25902  * Tab pane
25903  * 
25904  */
25905 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25906 /**
25907  * @class Roo.bootstrap.TabPane
25908  * @extends Roo.bootstrap.Component
25909  * Bootstrap TabPane class
25910  * @cfg {Boolean} active (false | true) Default false
25911  * @cfg {String} title title of panel
25912
25913  * 
25914  * @constructor
25915  * Create a new TabPane
25916  * @param {Object} config The config object
25917  */
25918
25919 Roo.bootstrap.dash.TabPane = function(config){
25920     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25921     
25922     this.addEvents({
25923         // raw events
25924         /**
25925          * @event activate
25926          * When a pane is activated
25927          * @param {Roo.bootstrap.dash.TabPane} pane
25928          */
25929         "activate" : true
25930          
25931     });
25932 };
25933
25934 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25935     
25936     active : false,
25937     title : '',
25938     
25939     // the tabBox that this is attached to.
25940     tab : false,
25941      
25942     getAutoCreate : function() 
25943     {
25944         var cfg = {
25945             tag: 'div',
25946             cls: 'tab-pane'
25947         };
25948         
25949         if(this.active){
25950             cfg.cls += ' active';
25951         }
25952         
25953         return cfg;
25954     },
25955     initEvents  : function()
25956     {
25957         //Roo.log('trigger add pane handler');
25958         this.parent().fireEvent('addpane', this)
25959     },
25960     
25961      /**
25962      * Updates the tab title 
25963      * @param {String} html to set the title to.
25964      */
25965     setTitle: function(str)
25966     {
25967         if (!this.tab) {
25968             return;
25969         }
25970         this.title = str;
25971         this.tab.select('a', true).first().dom.innerHTML = str;
25972         
25973     }
25974     
25975     
25976     
25977 });
25978
25979  
25980
25981
25982  /*
25983  * - LGPL
25984  *
25985  * menu
25986  * 
25987  */
25988 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25989
25990 /**
25991  * @class Roo.bootstrap.menu.Menu
25992  * @extends Roo.bootstrap.Component
25993  * Bootstrap Menu class - container for Menu
25994  * @cfg {String} html Text of the menu
25995  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25996  * @cfg {String} icon Font awesome icon
25997  * @cfg {String} pos Menu align to (top | bottom) default bottom
25998  * 
25999  * 
26000  * @constructor
26001  * Create a new Menu
26002  * @param {Object} config The config object
26003  */
26004
26005
26006 Roo.bootstrap.menu.Menu = function(config){
26007     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
26008     
26009     this.addEvents({
26010         /**
26011          * @event beforeshow
26012          * Fires before this menu is displayed
26013          * @param {Roo.bootstrap.menu.Menu} this
26014          */
26015         beforeshow : true,
26016         /**
26017          * @event beforehide
26018          * Fires before this menu is hidden
26019          * @param {Roo.bootstrap.menu.Menu} this
26020          */
26021         beforehide : true,
26022         /**
26023          * @event show
26024          * Fires after this menu is displayed
26025          * @param {Roo.bootstrap.menu.Menu} this
26026          */
26027         show : true,
26028         /**
26029          * @event hide
26030          * Fires after this menu is hidden
26031          * @param {Roo.bootstrap.menu.Menu} this
26032          */
26033         hide : true,
26034         /**
26035          * @event click
26036          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
26037          * @param {Roo.bootstrap.menu.Menu} this
26038          * @param {Roo.EventObject} e
26039          */
26040         click : true
26041     });
26042     
26043 };
26044
26045 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
26046     
26047     submenu : false,
26048     html : '',
26049     weight : 'default',
26050     icon : false,
26051     pos : 'bottom',
26052     
26053     
26054     getChildContainer : function() {
26055         if(this.isSubMenu){
26056             return this.el;
26057         }
26058         
26059         return this.el.select('ul.dropdown-menu', true).first();  
26060     },
26061     
26062     getAutoCreate : function()
26063     {
26064         var text = [
26065             {
26066                 tag : 'span',
26067                 cls : 'roo-menu-text',
26068                 html : this.html
26069             }
26070         ];
26071         
26072         if(this.icon){
26073             text.unshift({
26074                 tag : 'i',
26075                 cls : 'fa ' + this.icon
26076             })
26077         }
26078         
26079         
26080         var cfg = {
26081             tag : 'div',
26082             cls : 'btn-group',
26083             cn : [
26084                 {
26085                     tag : 'button',
26086                     cls : 'dropdown-button btn btn-' + this.weight,
26087                     cn : text
26088                 },
26089                 {
26090                     tag : 'button',
26091                     cls : 'dropdown-toggle btn btn-' + this.weight,
26092                     cn : [
26093                         {
26094                             tag : 'span',
26095                             cls : 'caret'
26096                         }
26097                     ]
26098                 },
26099                 {
26100                     tag : 'ul',
26101                     cls : 'dropdown-menu'
26102                 }
26103             ]
26104             
26105         };
26106         
26107         if(this.pos == 'top'){
26108             cfg.cls += ' dropup';
26109         }
26110         
26111         if(this.isSubMenu){
26112             cfg = {
26113                 tag : 'ul',
26114                 cls : 'dropdown-menu'
26115             }
26116         }
26117         
26118         return cfg;
26119     },
26120     
26121     onRender : function(ct, position)
26122     {
26123         this.isSubMenu = ct.hasClass('dropdown-submenu');
26124         
26125         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
26126     },
26127     
26128     initEvents : function() 
26129     {
26130         if(this.isSubMenu){
26131             return;
26132         }
26133         
26134         this.hidden = true;
26135         
26136         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
26137         this.triggerEl.on('click', this.onTriggerPress, this);
26138         
26139         this.buttonEl = this.el.select('button.dropdown-button', true).first();
26140         this.buttonEl.on('click', this.onClick, this);
26141         
26142     },
26143     
26144     list : function()
26145     {
26146         if(this.isSubMenu){
26147             return this.el;
26148         }
26149         
26150         return this.el.select('ul.dropdown-menu', true).first();
26151     },
26152     
26153     onClick : function(e)
26154     {
26155         this.fireEvent("click", this, e);
26156     },
26157     
26158     onTriggerPress  : function(e)
26159     {   
26160         if (this.isVisible()) {
26161             this.hide();
26162         } else {
26163             this.show();
26164         }
26165     },
26166     
26167     isVisible : function(){
26168         return !this.hidden;
26169     },
26170     
26171     show : function()
26172     {
26173         this.fireEvent("beforeshow", this);
26174         
26175         this.hidden = false;
26176         this.el.addClass('open');
26177         
26178         Roo.get(document).on("mouseup", this.onMouseUp, this);
26179         
26180         this.fireEvent("show", this);
26181         
26182         
26183     },
26184     
26185     hide : function()
26186     {
26187         this.fireEvent("beforehide", this);
26188         
26189         this.hidden = true;
26190         this.el.removeClass('open');
26191         
26192         Roo.get(document).un("mouseup", this.onMouseUp);
26193         
26194         this.fireEvent("hide", this);
26195     },
26196     
26197     onMouseUp : function()
26198     {
26199         this.hide();
26200     }
26201     
26202 });
26203
26204  
26205  /*
26206  * - LGPL
26207  *
26208  * menu item
26209  * 
26210  */
26211 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26212
26213 /**
26214  * @class Roo.bootstrap.menu.Item
26215  * @extends Roo.bootstrap.Component
26216  * Bootstrap MenuItem class
26217  * @cfg {Boolean} submenu (true | false) default false
26218  * @cfg {String} html text of the item
26219  * @cfg {String} href the link
26220  * @cfg {Boolean} disable (true | false) default false
26221  * @cfg {Boolean} preventDefault (true | false) default true
26222  * @cfg {String} icon Font awesome icon
26223  * @cfg {String} pos Submenu align to (left | right) default right 
26224  * 
26225  * 
26226  * @constructor
26227  * Create a new Item
26228  * @param {Object} config The config object
26229  */
26230
26231
26232 Roo.bootstrap.menu.Item = function(config){
26233     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
26234     this.addEvents({
26235         /**
26236          * @event mouseover
26237          * Fires when the mouse is hovering over this menu
26238          * @param {Roo.bootstrap.menu.Item} this
26239          * @param {Roo.EventObject} e
26240          */
26241         mouseover : true,
26242         /**
26243          * @event mouseout
26244          * Fires when the mouse exits this menu
26245          * @param {Roo.bootstrap.menu.Item} this
26246          * @param {Roo.EventObject} e
26247          */
26248         mouseout : true,
26249         // raw events
26250         /**
26251          * @event click
26252          * The raw click event for the entire grid.
26253          * @param {Roo.EventObject} e
26254          */
26255         click : true
26256     });
26257 };
26258
26259 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
26260     
26261     submenu : false,
26262     href : '',
26263     html : '',
26264     preventDefault: true,
26265     disable : false,
26266     icon : false,
26267     pos : 'right',
26268     
26269     getAutoCreate : function()
26270     {
26271         var text = [
26272             {
26273                 tag : 'span',
26274                 cls : 'roo-menu-item-text',
26275                 html : this.html
26276             }
26277         ];
26278         
26279         if(this.icon){
26280             text.unshift({
26281                 tag : 'i',
26282                 cls : 'fa ' + this.icon
26283             })
26284         }
26285         
26286         var cfg = {
26287             tag : 'li',
26288             cn : [
26289                 {
26290                     tag : 'a',
26291                     href : this.href || '#',
26292                     cn : text
26293                 }
26294             ]
26295         };
26296         
26297         if(this.disable){
26298             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
26299         }
26300         
26301         if(this.submenu){
26302             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
26303             
26304             if(this.pos == 'left'){
26305                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
26306             }
26307         }
26308         
26309         return cfg;
26310     },
26311     
26312     initEvents : function() 
26313     {
26314         this.el.on('mouseover', this.onMouseOver, this);
26315         this.el.on('mouseout', this.onMouseOut, this);
26316         
26317         this.el.select('a', true).first().on('click', this.onClick, this);
26318         
26319     },
26320     
26321     onClick : function(e)
26322     {
26323         if(this.preventDefault){
26324             e.preventDefault();
26325         }
26326         
26327         this.fireEvent("click", this, e);
26328     },
26329     
26330     onMouseOver : function(e)
26331     {
26332         if(this.submenu && this.pos == 'left'){
26333             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
26334         }
26335         
26336         this.fireEvent("mouseover", this, e);
26337     },
26338     
26339     onMouseOut : function(e)
26340     {
26341         this.fireEvent("mouseout", this, e);
26342     }
26343 });
26344
26345  
26346
26347  /*
26348  * - LGPL
26349  *
26350  * menu separator
26351  * 
26352  */
26353 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
26354
26355 /**
26356  * @class Roo.bootstrap.menu.Separator
26357  * @extends Roo.bootstrap.Component
26358  * Bootstrap Separator class
26359  * 
26360  * @constructor
26361  * Create a new Separator
26362  * @param {Object} config The config object
26363  */
26364
26365
26366 Roo.bootstrap.menu.Separator = function(config){
26367     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
26368 };
26369
26370 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
26371     
26372     getAutoCreate : function(){
26373         var cfg = {
26374             tag : 'li',
26375             cls: 'divider'
26376         };
26377         
26378         return cfg;
26379     }
26380    
26381 });
26382
26383  
26384
26385  /*
26386  * - LGPL
26387  *
26388  * Tooltip
26389  * 
26390  */
26391
26392 /**
26393  * @class Roo.bootstrap.Tooltip
26394  * Bootstrap Tooltip class
26395  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
26396  * to determine which dom element triggers the tooltip.
26397  * 
26398  * It needs to add support for additional attributes like tooltip-position
26399  * 
26400  * @constructor
26401  * Create a new Toolti
26402  * @param {Object} config The config object
26403  */
26404
26405 Roo.bootstrap.Tooltip = function(config){
26406     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
26407     
26408     this.alignment = Roo.bootstrap.Tooltip.alignment;
26409     
26410     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
26411         this.alignment = config.alignment;
26412     }
26413     
26414 };
26415
26416 Roo.apply(Roo.bootstrap.Tooltip, {
26417     /**
26418      * @function init initialize tooltip monitoring.
26419      * @static
26420      */
26421     currentEl : false,
26422     currentTip : false,
26423     currentRegion : false,
26424     
26425     //  init : delay?
26426     
26427     init : function()
26428     {
26429         Roo.get(document).on('mouseover', this.enter ,this);
26430         Roo.get(document).on('mouseout', this.leave, this);
26431          
26432         
26433         this.currentTip = new Roo.bootstrap.Tooltip();
26434     },
26435     
26436     enter : function(ev)
26437     {
26438         var dom = ev.getTarget();
26439         
26440         //Roo.log(['enter',dom]);
26441         var el = Roo.fly(dom);
26442         if (this.currentEl) {
26443             //Roo.log(dom);
26444             //Roo.log(this.currentEl);
26445             //Roo.log(this.currentEl.contains(dom));
26446             if (this.currentEl == el) {
26447                 return;
26448             }
26449             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
26450                 return;
26451             }
26452
26453         }
26454         
26455         if (this.currentTip.el) {
26456             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
26457         }    
26458         //Roo.log(ev);
26459         
26460         if(!el || el.dom == document){
26461             return;
26462         }
26463         
26464         var bindEl = el;
26465         
26466         // you can not look for children, as if el is the body.. then everythign is the child..
26467         if (!el.attr('tooltip')) { //
26468             if (!el.select("[tooltip]").elements.length) {
26469                 return;
26470             }
26471             // is the mouse over this child...?
26472             bindEl = el.select("[tooltip]").first();
26473             var xy = ev.getXY();
26474             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
26475                 //Roo.log("not in region.");
26476                 return;
26477             }
26478             //Roo.log("child element over..");
26479             
26480         }
26481         this.currentEl = bindEl;
26482         this.currentTip.bind(bindEl);
26483         this.currentRegion = Roo.lib.Region.getRegion(dom);
26484         this.currentTip.enter();
26485         
26486     },
26487     leave : function(ev)
26488     {
26489         var dom = ev.getTarget();
26490         //Roo.log(['leave',dom]);
26491         if (!this.currentEl) {
26492             return;
26493         }
26494         
26495         
26496         if (dom != this.currentEl.dom) {
26497             return;
26498         }
26499         var xy = ev.getXY();
26500         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
26501             return;
26502         }
26503         // only activate leave if mouse cursor is outside... bounding box..
26504         
26505         
26506         
26507         
26508         if (this.currentTip) {
26509             this.currentTip.leave();
26510         }
26511         //Roo.log('clear currentEl');
26512         this.currentEl = false;
26513         
26514         
26515     },
26516     alignment : {
26517         'left' : ['r-l', [-2,0], 'right'],
26518         'right' : ['l-r', [2,0], 'left'],
26519         'bottom' : ['t-b', [0,2], 'top'],
26520         'top' : [ 'b-t', [0,-2], 'bottom']
26521     }
26522     
26523 });
26524
26525
26526 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
26527     
26528     
26529     bindEl : false,
26530     
26531     delay : null, // can be { show : 300 , hide: 500}
26532     
26533     timeout : null,
26534     
26535     hoverState : null, //???
26536     
26537     placement : 'bottom', 
26538     
26539     alignment : false,
26540     
26541     getAutoCreate : function(){
26542     
26543         var cfg = {
26544            cls : 'tooltip',
26545            role : 'tooltip',
26546            cn : [
26547                 {
26548                     cls : 'tooltip-arrow'
26549                 },
26550                 {
26551                     cls : 'tooltip-inner'
26552                 }
26553            ]
26554         };
26555         
26556         return cfg;
26557     },
26558     bind : function(el)
26559     {
26560         this.bindEl = el;
26561     },
26562       
26563     
26564     enter : function () {
26565        
26566         if (this.timeout != null) {
26567             clearTimeout(this.timeout);
26568         }
26569         
26570         this.hoverState = 'in';
26571          //Roo.log("enter - show");
26572         if (!this.delay || !this.delay.show) {
26573             this.show();
26574             return;
26575         }
26576         var _t = this;
26577         this.timeout = setTimeout(function () {
26578             if (_t.hoverState == 'in') {
26579                 _t.show();
26580             }
26581         }, this.delay.show);
26582     },
26583     leave : function()
26584     {
26585         clearTimeout(this.timeout);
26586     
26587         this.hoverState = 'out';
26588          if (!this.delay || !this.delay.hide) {
26589             this.hide();
26590             return;
26591         }
26592        
26593         var _t = this;
26594         this.timeout = setTimeout(function () {
26595             //Roo.log("leave - timeout");
26596             
26597             if (_t.hoverState == 'out') {
26598                 _t.hide();
26599                 Roo.bootstrap.Tooltip.currentEl = false;
26600             }
26601         }, delay);
26602     },
26603     
26604     show : function (msg)
26605     {
26606         if (!this.el) {
26607             this.render(document.body);
26608         }
26609         // set content.
26610         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
26611         
26612         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
26613         
26614         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
26615         
26616         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
26617         
26618         var placement = typeof this.placement == 'function' ?
26619             this.placement.call(this, this.el, on_el) :
26620             this.placement;
26621             
26622         var autoToken = /\s?auto?\s?/i;
26623         var autoPlace = autoToken.test(placement);
26624         if (autoPlace) {
26625             placement = placement.replace(autoToken, '') || 'top';
26626         }
26627         
26628         //this.el.detach()
26629         //this.el.setXY([0,0]);
26630         this.el.show();
26631         //this.el.dom.style.display='block';
26632         
26633         //this.el.appendTo(on_el);
26634         
26635         var p = this.getPosition();
26636         var box = this.el.getBox();
26637         
26638         if (autoPlace) {
26639             // fixme..
26640         }
26641         
26642         var align = this.alignment[placement];
26643         
26644         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
26645         
26646         if(placement == 'top' || placement == 'bottom'){
26647             if(xy[0] < 0){
26648                 placement = 'right';
26649             }
26650             
26651             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
26652                 placement = 'left';
26653             }
26654             
26655             var scroll = Roo.select('body', true).first().getScroll();
26656             
26657             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26658                 placement = 'top';
26659             }
26660             
26661             align = this.alignment[placement];
26662         }
26663         
26664         this.el.alignTo(this.bindEl, align[0],align[1]);
26665         //var arrow = this.el.select('.arrow',true).first();
26666         //arrow.set(align[2], 
26667         
26668         this.el.addClass(placement);
26669         
26670         this.el.addClass('in fade');
26671         
26672         this.hoverState = null;
26673         
26674         if (this.el.hasClass('fade')) {
26675             // fade it?
26676         }
26677         
26678     },
26679     hide : function()
26680     {
26681          
26682         if (!this.el) {
26683             return;
26684         }
26685         //this.el.setXY([0,0]);
26686         this.el.removeClass('in');
26687         //this.el.hide();
26688         
26689     }
26690     
26691 });
26692  
26693
26694  /*
26695  * - LGPL
26696  *
26697  * Location Picker
26698  * 
26699  */
26700
26701 /**
26702  * @class Roo.bootstrap.LocationPicker
26703  * @extends Roo.bootstrap.Component
26704  * Bootstrap LocationPicker class
26705  * @cfg {Number} latitude Position when init default 0
26706  * @cfg {Number} longitude Position when init default 0
26707  * @cfg {Number} zoom default 15
26708  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26709  * @cfg {Boolean} mapTypeControl default false
26710  * @cfg {Boolean} disableDoubleClickZoom default false
26711  * @cfg {Boolean} scrollwheel default true
26712  * @cfg {Boolean} streetViewControl default false
26713  * @cfg {Number} radius default 0
26714  * @cfg {String} locationName
26715  * @cfg {Boolean} draggable default true
26716  * @cfg {Boolean} enableAutocomplete default false
26717  * @cfg {Boolean} enableReverseGeocode default true
26718  * @cfg {String} markerTitle
26719  * 
26720  * @constructor
26721  * Create a new LocationPicker
26722  * @param {Object} config The config object
26723  */
26724
26725
26726 Roo.bootstrap.LocationPicker = function(config){
26727     
26728     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26729     
26730     this.addEvents({
26731         /**
26732          * @event initial
26733          * Fires when the picker initialized.
26734          * @param {Roo.bootstrap.LocationPicker} this
26735          * @param {Google Location} location
26736          */
26737         initial : true,
26738         /**
26739          * @event positionchanged
26740          * Fires when the picker position changed.
26741          * @param {Roo.bootstrap.LocationPicker} this
26742          * @param {Google Location} location
26743          */
26744         positionchanged : true,
26745         /**
26746          * @event resize
26747          * Fires when the map resize.
26748          * @param {Roo.bootstrap.LocationPicker} this
26749          */
26750         resize : true,
26751         /**
26752          * @event show
26753          * Fires when the map show.
26754          * @param {Roo.bootstrap.LocationPicker} this
26755          */
26756         show : true,
26757         /**
26758          * @event hide
26759          * Fires when the map hide.
26760          * @param {Roo.bootstrap.LocationPicker} this
26761          */
26762         hide : true,
26763         /**
26764          * @event mapClick
26765          * Fires when click the map.
26766          * @param {Roo.bootstrap.LocationPicker} this
26767          * @param {Map event} e
26768          */
26769         mapClick : true,
26770         /**
26771          * @event mapRightClick
26772          * Fires when right click the map.
26773          * @param {Roo.bootstrap.LocationPicker} this
26774          * @param {Map event} e
26775          */
26776         mapRightClick : true,
26777         /**
26778          * @event markerClick
26779          * Fires when click the marker.
26780          * @param {Roo.bootstrap.LocationPicker} this
26781          * @param {Map event} e
26782          */
26783         markerClick : true,
26784         /**
26785          * @event markerRightClick
26786          * Fires when right click the marker.
26787          * @param {Roo.bootstrap.LocationPicker} this
26788          * @param {Map event} e
26789          */
26790         markerRightClick : true,
26791         /**
26792          * @event OverlayViewDraw
26793          * Fires when OverlayView Draw
26794          * @param {Roo.bootstrap.LocationPicker} this
26795          */
26796         OverlayViewDraw : true,
26797         /**
26798          * @event OverlayViewOnAdd
26799          * Fires when OverlayView Draw
26800          * @param {Roo.bootstrap.LocationPicker} this
26801          */
26802         OverlayViewOnAdd : true,
26803         /**
26804          * @event OverlayViewOnRemove
26805          * Fires when OverlayView Draw
26806          * @param {Roo.bootstrap.LocationPicker} this
26807          */
26808         OverlayViewOnRemove : true,
26809         /**
26810          * @event OverlayViewShow
26811          * Fires when OverlayView Draw
26812          * @param {Roo.bootstrap.LocationPicker} this
26813          * @param {Pixel} cpx
26814          */
26815         OverlayViewShow : true,
26816         /**
26817          * @event OverlayViewHide
26818          * Fires when OverlayView Draw
26819          * @param {Roo.bootstrap.LocationPicker} this
26820          */
26821         OverlayViewHide : true,
26822         /**
26823          * @event loadexception
26824          * Fires when load google lib failed.
26825          * @param {Roo.bootstrap.LocationPicker} this
26826          */
26827         loadexception : true
26828     });
26829         
26830 };
26831
26832 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26833     
26834     gMapContext: false,
26835     
26836     latitude: 0,
26837     longitude: 0,
26838     zoom: 15,
26839     mapTypeId: false,
26840     mapTypeControl: false,
26841     disableDoubleClickZoom: false,
26842     scrollwheel: true,
26843     streetViewControl: false,
26844     radius: 0,
26845     locationName: '',
26846     draggable: true,
26847     enableAutocomplete: false,
26848     enableReverseGeocode: true,
26849     markerTitle: '',
26850     
26851     getAutoCreate: function()
26852     {
26853
26854         var cfg = {
26855             tag: 'div',
26856             cls: 'roo-location-picker'
26857         };
26858         
26859         return cfg
26860     },
26861     
26862     initEvents: function(ct, position)
26863     {       
26864         if(!this.el.getWidth() || this.isApplied()){
26865             return;
26866         }
26867         
26868         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26869         
26870         this.initial();
26871     },
26872     
26873     initial: function()
26874     {
26875         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26876             this.fireEvent('loadexception', this);
26877             return;
26878         }
26879         
26880         if(!this.mapTypeId){
26881             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26882         }
26883         
26884         this.gMapContext = this.GMapContext();
26885         
26886         this.initOverlayView();
26887         
26888         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26889         
26890         var _this = this;
26891                 
26892         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26893             _this.setPosition(_this.gMapContext.marker.position);
26894         });
26895         
26896         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26897             _this.fireEvent('mapClick', this, event);
26898             
26899         });
26900
26901         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26902             _this.fireEvent('mapRightClick', this, event);
26903             
26904         });
26905         
26906         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26907             _this.fireEvent('markerClick', this, event);
26908             
26909         });
26910
26911         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26912             _this.fireEvent('markerRightClick', this, event);
26913             
26914         });
26915         
26916         this.setPosition(this.gMapContext.location);
26917         
26918         this.fireEvent('initial', this, this.gMapContext.location);
26919     },
26920     
26921     initOverlayView: function()
26922     {
26923         var _this = this;
26924         
26925         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26926             
26927             draw: function()
26928             {
26929                 _this.fireEvent('OverlayViewDraw', _this);
26930             },
26931             
26932             onAdd: function()
26933             {
26934                 _this.fireEvent('OverlayViewOnAdd', _this);
26935             },
26936             
26937             onRemove: function()
26938             {
26939                 _this.fireEvent('OverlayViewOnRemove', _this);
26940             },
26941             
26942             show: function(cpx)
26943             {
26944                 _this.fireEvent('OverlayViewShow', _this, cpx);
26945             },
26946             
26947             hide: function()
26948             {
26949                 _this.fireEvent('OverlayViewHide', _this);
26950             }
26951             
26952         });
26953     },
26954     
26955     fromLatLngToContainerPixel: function(event)
26956     {
26957         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26958     },
26959     
26960     isApplied: function() 
26961     {
26962         return this.getGmapContext() == false ? false : true;
26963     },
26964     
26965     getGmapContext: function() 
26966     {
26967         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26968     },
26969     
26970     GMapContext: function() 
26971     {
26972         var position = new google.maps.LatLng(this.latitude, this.longitude);
26973         
26974         var _map = new google.maps.Map(this.el.dom, {
26975             center: position,
26976             zoom: this.zoom,
26977             mapTypeId: this.mapTypeId,
26978             mapTypeControl: this.mapTypeControl,
26979             disableDoubleClickZoom: this.disableDoubleClickZoom,
26980             scrollwheel: this.scrollwheel,
26981             streetViewControl: this.streetViewControl,
26982             locationName: this.locationName,
26983             draggable: this.draggable,
26984             enableAutocomplete: this.enableAutocomplete,
26985             enableReverseGeocode: this.enableReverseGeocode
26986         });
26987         
26988         var _marker = new google.maps.Marker({
26989             position: position,
26990             map: _map,
26991             title: this.markerTitle,
26992             draggable: this.draggable
26993         });
26994         
26995         return {
26996             map: _map,
26997             marker: _marker,
26998             circle: null,
26999             location: position,
27000             radius: this.radius,
27001             locationName: this.locationName,
27002             addressComponents: {
27003                 formatted_address: null,
27004                 addressLine1: null,
27005                 addressLine2: null,
27006                 streetName: null,
27007                 streetNumber: null,
27008                 city: null,
27009                 district: null,
27010                 state: null,
27011                 stateOrProvince: null
27012             },
27013             settings: this,
27014             domContainer: this.el.dom,
27015             geodecoder: new google.maps.Geocoder()
27016         };
27017     },
27018     
27019     drawCircle: function(center, radius, options) 
27020     {
27021         if (this.gMapContext.circle != null) {
27022             this.gMapContext.circle.setMap(null);
27023         }
27024         if (radius > 0) {
27025             radius *= 1;
27026             options = Roo.apply({}, options, {
27027                 strokeColor: "#0000FF",
27028                 strokeOpacity: .35,
27029                 strokeWeight: 2,
27030                 fillColor: "#0000FF",
27031                 fillOpacity: .2
27032             });
27033             
27034             options.map = this.gMapContext.map;
27035             options.radius = radius;
27036             options.center = center;
27037             this.gMapContext.circle = new google.maps.Circle(options);
27038             return this.gMapContext.circle;
27039         }
27040         
27041         return null;
27042     },
27043     
27044     setPosition: function(location) 
27045     {
27046         this.gMapContext.location = location;
27047         this.gMapContext.marker.setPosition(location);
27048         this.gMapContext.map.panTo(location);
27049         this.drawCircle(location, this.gMapContext.radius, {});
27050         
27051         var _this = this;
27052         
27053         if (this.gMapContext.settings.enableReverseGeocode) {
27054             this.gMapContext.geodecoder.geocode({
27055                 latLng: this.gMapContext.location
27056             }, function(results, status) {
27057                 
27058                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
27059                     _this.gMapContext.locationName = results[0].formatted_address;
27060                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
27061                     
27062                     _this.fireEvent('positionchanged', this, location);
27063                 }
27064             });
27065             
27066             return;
27067         }
27068         
27069         this.fireEvent('positionchanged', this, location);
27070     },
27071     
27072     resize: function()
27073     {
27074         google.maps.event.trigger(this.gMapContext.map, "resize");
27075         
27076         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
27077         
27078         this.fireEvent('resize', this);
27079     },
27080     
27081     setPositionByLatLng: function(latitude, longitude)
27082     {
27083         this.setPosition(new google.maps.LatLng(latitude, longitude));
27084     },
27085     
27086     getCurrentPosition: function() 
27087     {
27088         return {
27089             latitude: this.gMapContext.location.lat(),
27090             longitude: this.gMapContext.location.lng()
27091         };
27092     },
27093     
27094     getAddressName: function() 
27095     {
27096         return this.gMapContext.locationName;
27097     },
27098     
27099     getAddressComponents: function() 
27100     {
27101         return this.gMapContext.addressComponents;
27102     },
27103     
27104     address_component_from_google_geocode: function(address_components) 
27105     {
27106         var result = {};
27107         
27108         for (var i = 0; i < address_components.length; i++) {
27109             var component = address_components[i];
27110             if (component.types.indexOf("postal_code") >= 0) {
27111                 result.postalCode = component.short_name;
27112             } else if (component.types.indexOf("street_number") >= 0) {
27113                 result.streetNumber = component.short_name;
27114             } else if (component.types.indexOf("route") >= 0) {
27115                 result.streetName = component.short_name;
27116             } else if (component.types.indexOf("neighborhood") >= 0) {
27117                 result.city = component.short_name;
27118             } else if (component.types.indexOf("locality") >= 0) {
27119                 result.city = component.short_name;
27120             } else if (component.types.indexOf("sublocality") >= 0) {
27121                 result.district = component.short_name;
27122             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
27123                 result.stateOrProvince = component.short_name;
27124             } else if (component.types.indexOf("country") >= 0) {
27125                 result.country = component.short_name;
27126             }
27127         }
27128         
27129         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
27130         result.addressLine2 = "";
27131         return result;
27132     },
27133     
27134     setZoomLevel: function(zoom)
27135     {
27136         this.gMapContext.map.setZoom(zoom);
27137     },
27138     
27139     show: function()
27140     {
27141         if(!this.el){
27142             return;
27143         }
27144         
27145         this.el.show();
27146         
27147         this.resize();
27148         
27149         this.fireEvent('show', this);
27150     },
27151     
27152     hide: function()
27153     {
27154         if(!this.el){
27155             return;
27156         }
27157         
27158         this.el.hide();
27159         
27160         this.fireEvent('hide', this);
27161     }
27162     
27163 });
27164
27165 Roo.apply(Roo.bootstrap.LocationPicker, {
27166     
27167     OverlayView : function(map, options)
27168     {
27169         options = options || {};
27170         
27171         this.setMap(map);
27172     }
27173     
27174     
27175 });/*
27176  * - LGPL
27177  *
27178  * Alert
27179  * 
27180  */
27181
27182 /**
27183  * @class Roo.bootstrap.Alert
27184  * @extends Roo.bootstrap.Component
27185  * Bootstrap Alert class
27186  * @cfg {String} title The title of alert
27187  * @cfg {String} html The content of alert
27188  * @cfg {String} weight (  success | info | warning | danger )
27189  * @cfg {String} faicon font-awesomeicon
27190  * 
27191  * @constructor
27192  * Create a new alert
27193  * @param {Object} config The config object
27194  */
27195
27196
27197 Roo.bootstrap.Alert = function(config){
27198     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
27199     
27200 };
27201
27202 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
27203     
27204     title: '',
27205     html: '',
27206     weight: false,
27207     faicon: false,
27208     
27209     getAutoCreate : function()
27210     {
27211         
27212         var cfg = {
27213             tag : 'div',
27214             cls : 'alert',
27215             cn : [
27216                 {
27217                     tag : 'i',
27218                     cls : 'roo-alert-icon'
27219                     
27220                 },
27221                 {
27222                     tag : 'b',
27223                     cls : 'roo-alert-title',
27224                     html : this.title
27225                 },
27226                 {
27227                     tag : 'span',
27228                     cls : 'roo-alert-text',
27229                     html : this.html
27230                 }
27231             ]
27232         };
27233         
27234         if(this.faicon){
27235             cfg.cn[0].cls += ' fa ' + this.faicon;
27236         }
27237         
27238         if(this.weight){
27239             cfg.cls += ' alert-' + this.weight;
27240         }
27241         
27242         return cfg;
27243     },
27244     
27245     initEvents: function() 
27246     {
27247         this.el.setVisibilityMode(Roo.Element.DISPLAY);
27248     },
27249     
27250     setTitle : function(str)
27251     {
27252         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
27253     },
27254     
27255     setText : function(str)
27256     {
27257         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
27258     },
27259     
27260     setWeight : function(weight)
27261     {
27262         if(this.weight){
27263             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
27264         }
27265         
27266         this.weight = weight;
27267         
27268         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
27269     },
27270     
27271     setIcon : function(icon)
27272     {
27273         if(this.faicon){
27274             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
27275         }
27276         
27277         this.faicon = icon;
27278         
27279         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
27280     },
27281     
27282     hide: function() 
27283     {
27284         this.el.hide();   
27285     },
27286     
27287     show: function() 
27288     {  
27289         this.el.show();   
27290     }
27291     
27292 });
27293
27294  
27295 /*
27296 * Licence: LGPL
27297 */
27298
27299 /**
27300  * @class Roo.bootstrap.UploadCropbox
27301  * @extends Roo.bootstrap.Component
27302  * Bootstrap UploadCropbox class
27303  * @cfg {String} emptyText show when image has been loaded
27304  * @cfg {String} rotateNotify show when image too small to rotate
27305  * @cfg {Number} errorTimeout default 3000
27306  * @cfg {Number} minWidth default 300
27307  * @cfg {Number} minHeight default 300
27308  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
27309  * @cfg {Boolean} isDocument (true|false) default false
27310  * @cfg {String} url action url
27311  * @cfg {String} paramName default 'imageUpload'
27312  * @cfg {String} method default POST
27313  * @cfg {Boolean} loadMask (true|false) default true
27314  * @cfg {Boolean} loadingText default 'Loading...'
27315  * 
27316  * @constructor
27317  * Create a new UploadCropbox
27318  * @param {Object} config The config object
27319  */
27320
27321 Roo.bootstrap.UploadCropbox = function(config){
27322     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
27323     
27324     this.addEvents({
27325         /**
27326          * @event beforeselectfile
27327          * Fire before select file
27328          * @param {Roo.bootstrap.UploadCropbox} this
27329          */
27330         "beforeselectfile" : true,
27331         /**
27332          * @event initial
27333          * Fire after initEvent
27334          * @param {Roo.bootstrap.UploadCropbox} this
27335          */
27336         "initial" : true,
27337         /**
27338          * @event crop
27339          * Fire after initEvent
27340          * @param {Roo.bootstrap.UploadCropbox} this
27341          * @param {String} data
27342          */
27343         "crop" : true,
27344         /**
27345          * @event prepare
27346          * Fire when preparing the file data
27347          * @param {Roo.bootstrap.UploadCropbox} this
27348          * @param {Object} file
27349          */
27350         "prepare" : true,
27351         /**
27352          * @event exception
27353          * Fire when get exception
27354          * @param {Roo.bootstrap.UploadCropbox} this
27355          * @param {XMLHttpRequest} xhr
27356          */
27357         "exception" : true,
27358         /**
27359          * @event beforeloadcanvas
27360          * Fire before load the canvas
27361          * @param {Roo.bootstrap.UploadCropbox} this
27362          * @param {String} src
27363          */
27364         "beforeloadcanvas" : true,
27365         /**
27366          * @event trash
27367          * Fire when trash image
27368          * @param {Roo.bootstrap.UploadCropbox} this
27369          */
27370         "trash" : true,
27371         /**
27372          * @event download
27373          * Fire when download the image
27374          * @param {Roo.bootstrap.UploadCropbox} this
27375          */
27376         "download" : true,
27377         /**
27378          * @event footerbuttonclick
27379          * Fire when footerbuttonclick
27380          * @param {Roo.bootstrap.UploadCropbox} this
27381          * @param {String} type
27382          */
27383         "footerbuttonclick" : true,
27384         /**
27385          * @event resize
27386          * Fire when resize
27387          * @param {Roo.bootstrap.UploadCropbox} this
27388          */
27389         "resize" : true,
27390         /**
27391          * @event rotate
27392          * Fire when rotate the image
27393          * @param {Roo.bootstrap.UploadCropbox} this
27394          * @param {String} pos
27395          */
27396         "rotate" : true,
27397         /**
27398          * @event inspect
27399          * Fire when inspect the file
27400          * @param {Roo.bootstrap.UploadCropbox} this
27401          * @param {Object} file
27402          */
27403         "inspect" : true,
27404         /**
27405          * @event upload
27406          * Fire when xhr upload the file
27407          * @param {Roo.bootstrap.UploadCropbox} this
27408          * @param {Object} data
27409          */
27410         "upload" : true,
27411         /**
27412          * @event arrange
27413          * Fire when arrange the file data
27414          * @param {Roo.bootstrap.UploadCropbox} this
27415          * @param {Object} formData
27416          */
27417         "arrange" : true
27418     });
27419     
27420     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
27421 };
27422
27423 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
27424     
27425     emptyText : 'Click to upload image',
27426     rotateNotify : 'Image is too small to rotate',
27427     errorTimeout : 3000,
27428     scale : 0,
27429     baseScale : 1,
27430     rotate : 0,
27431     dragable : false,
27432     pinching : false,
27433     mouseX : 0,
27434     mouseY : 0,
27435     cropData : false,
27436     minWidth : 300,
27437     minHeight : 300,
27438     file : false,
27439     exif : {},
27440     baseRotate : 1,
27441     cropType : 'image/jpeg',
27442     buttons : false,
27443     canvasLoaded : false,
27444     isDocument : false,
27445     method : 'POST',
27446     paramName : 'imageUpload',
27447     loadMask : true,
27448     loadingText : 'Loading...',
27449     maskEl : false,
27450     
27451     getAutoCreate : function()
27452     {
27453         var cfg = {
27454             tag : 'div',
27455             cls : 'roo-upload-cropbox',
27456             cn : [
27457                 {
27458                     tag : 'input',
27459                     cls : 'roo-upload-cropbox-selector',
27460                     type : 'file'
27461                 },
27462                 {
27463                     tag : 'div',
27464                     cls : 'roo-upload-cropbox-body',
27465                     style : 'cursor:pointer',
27466                     cn : [
27467                         {
27468                             tag : 'div',
27469                             cls : 'roo-upload-cropbox-preview'
27470                         },
27471                         {
27472                             tag : 'div',
27473                             cls : 'roo-upload-cropbox-thumb'
27474                         },
27475                         {
27476                             tag : 'div',
27477                             cls : 'roo-upload-cropbox-empty-notify',
27478                             html : this.emptyText
27479                         },
27480                         {
27481                             tag : 'div',
27482                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
27483                             html : this.rotateNotify
27484                         }
27485                     ]
27486                 },
27487                 {
27488                     tag : 'div',
27489                     cls : 'roo-upload-cropbox-footer',
27490                     cn : {
27491                         tag : 'div',
27492                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
27493                         cn : []
27494                     }
27495                 }
27496             ]
27497         };
27498         
27499         return cfg;
27500     },
27501     
27502     onRender : function(ct, position)
27503     {
27504         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
27505         
27506         if (this.buttons.length) {
27507             
27508             Roo.each(this.buttons, function(bb) {
27509                 
27510                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
27511                 
27512                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
27513                 
27514             }, this);
27515         }
27516         
27517         if(this.loadMask){
27518             this.maskEl = this.el;
27519         }
27520     },
27521     
27522     initEvents : function()
27523     {
27524         this.urlAPI = (window.createObjectURL && window) || 
27525                                 (window.URL && URL.revokeObjectURL && URL) || 
27526                                 (window.webkitURL && webkitURL);
27527                         
27528         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
27529         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27530         
27531         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
27532         this.selectorEl.hide();
27533         
27534         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
27535         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27536         
27537         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
27538         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27539         this.thumbEl.hide();
27540         
27541         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
27542         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27543         
27544         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
27545         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27546         this.errorEl.hide();
27547         
27548         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
27549         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27550         this.footerEl.hide();
27551         
27552         this.setThumbBoxSize();
27553         
27554         this.bind();
27555         
27556         this.resize();
27557         
27558         this.fireEvent('initial', this);
27559     },
27560
27561     bind : function()
27562     {
27563         var _this = this;
27564         
27565         window.addEventListener("resize", function() { _this.resize(); } );
27566         
27567         this.bodyEl.on('click', this.beforeSelectFile, this);
27568         
27569         if(Roo.isTouch){
27570             this.bodyEl.on('touchstart', this.onTouchStart, this);
27571             this.bodyEl.on('touchmove', this.onTouchMove, this);
27572             this.bodyEl.on('touchend', this.onTouchEnd, this);
27573         }
27574         
27575         if(!Roo.isTouch){
27576             this.bodyEl.on('mousedown', this.onMouseDown, this);
27577             this.bodyEl.on('mousemove', this.onMouseMove, this);
27578             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
27579             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
27580             Roo.get(document).on('mouseup', this.onMouseUp, this);
27581         }
27582         
27583         this.selectorEl.on('change', this.onFileSelected, this);
27584     },
27585     
27586     reset : function()
27587     {    
27588         this.scale = 0;
27589         this.baseScale = 1;
27590         this.rotate = 0;
27591         this.baseRotate = 1;
27592         this.dragable = false;
27593         this.pinching = false;
27594         this.mouseX = 0;
27595         this.mouseY = 0;
27596         this.cropData = false;
27597         this.notifyEl.dom.innerHTML = this.emptyText;
27598         
27599         this.selectorEl.dom.value = '';
27600         
27601     },
27602     
27603     resize : function()
27604     {
27605         if(this.fireEvent('resize', this) != false){
27606             this.setThumbBoxPosition();
27607             this.setCanvasPosition();
27608         }
27609     },
27610     
27611     onFooterButtonClick : function(e, el, o, type)
27612     {
27613         switch (type) {
27614             case 'rotate-left' :
27615                 this.onRotateLeft(e);
27616                 break;
27617             case 'rotate-right' :
27618                 this.onRotateRight(e);
27619                 break;
27620             case 'picture' :
27621                 this.beforeSelectFile(e);
27622                 break;
27623             case 'trash' :
27624                 this.trash(e);
27625                 break;
27626             case 'crop' :
27627                 this.crop(e);
27628                 break;
27629             case 'download' :
27630                 this.download(e);
27631                 break;
27632             default :
27633                 break;
27634         }
27635         
27636         this.fireEvent('footerbuttonclick', this, type);
27637     },
27638     
27639     beforeSelectFile : function(e)
27640     {
27641         e.preventDefault();
27642         
27643         if(this.fireEvent('beforeselectfile', this) != false){
27644             this.selectorEl.dom.click();
27645         }
27646     },
27647     
27648     onFileSelected : function(e)
27649     {
27650         e.preventDefault();
27651         
27652         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27653             return;
27654         }
27655         
27656         var file = this.selectorEl.dom.files[0];
27657         
27658         if(this.fireEvent('inspect', this, file) != false){
27659             this.prepare(file);
27660         }
27661         
27662     },
27663     
27664     trash : function(e)
27665     {
27666         this.fireEvent('trash', this);
27667     },
27668     
27669     download : function(e)
27670     {
27671         this.fireEvent('download', this);
27672     },
27673     
27674     loadCanvas : function(src)
27675     {   
27676         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27677             
27678             this.reset();
27679             
27680             this.imageEl = document.createElement('img');
27681             
27682             var _this = this;
27683             
27684             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27685             
27686             this.imageEl.src = src;
27687         }
27688     },
27689     
27690     onLoadCanvas : function()
27691     {   
27692         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27693         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27694         
27695         this.bodyEl.un('click', this.beforeSelectFile, this);
27696         
27697         this.notifyEl.hide();
27698         this.thumbEl.show();
27699         this.footerEl.show();
27700         
27701         this.baseRotateLevel();
27702         
27703         if(this.isDocument){
27704             this.setThumbBoxSize();
27705         }
27706         
27707         this.setThumbBoxPosition();
27708         
27709         this.baseScaleLevel();
27710         
27711         this.draw();
27712         
27713         this.resize();
27714         
27715         this.canvasLoaded = true;
27716         
27717         if(this.loadMask){
27718             this.maskEl.unmask();
27719         }
27720         
27721     },
27722     
27723     setCanvasPosition : function()
27724     {   
27725         if(!this.canvasEl){
27726             return;
27727         }
27728         
27729         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27730         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27731         
27732         this.previewEl.setLeft(pw);
27733         this.previewEl.setTop(ph);
27734         
27735     },
27736     
27737     onMouseDown : function(e)
27738     {   
27739         e.stopEvent();
27740         
27741         this.dragable = true;
27742         this.pinching = false;
27743         
27744         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27745             this.dragable = false;
27746             return;
27747         }
27748         
27749         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27750         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27751         
27752     },
27753     
27754     onMouseMove : function(e)
27755     {   
27756         e.stopEvent();
27757         
27758         if(!this.canvasLoaded){
27759             return;
27760         }
27761         
27762         if (!this.dragable){
27763             return;
27764         }
27765         
27766         var minX = Math.ceil(this.thumbEl.getLeft(true));
27767         var minY = Math.ceil(this.thumbEl.getTop(true));
27768         
27769         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27770         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27771         
27772         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27773         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27774         
27775         x = x - this.mouseX;
27776         y = y - this.mouseY;
27777         
27778         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27779         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27780         
27781         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27782         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27783         
27784         this.previewEl.setLeft(bgX);
27785         this.previewEl.setTop(bgY);
27786         
27787         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27788         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27789     },
27790     
27791     onMouseUp : function(e)
27792     {   
27793         e.stopEvent();
27794         
27795         this.dragable = false;
27796     },
27797     
27798     onMouseWheel : function(e)
27799     {   
27800         e.stopEvent();
27801         
27802         this.startScale = this.scale;
27803         
27804         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27805         
27806         if(!this.zoomable()){
27807             this.scale = this.startScale;
27808             return;
27809         }
27810         
27811         this.draw();
27812         
27813         return;
27814     },
27815     
27816     zoomable : function()
27817     {
27818         var minScale = this.thumbEl.getWidth() / this.minWidth;
27819         
27820         if(this.minWidth < this.minHeight){
27821             minScale = this.thumbEl.getHeight() / this.minHeight;
27822         }
27823         
27824         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27825         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27826         
27827         if(
27828                 this.isDocument &&
27829                 (this.rotate == 0 || this.rotate == 180) && 
27830                 (
27831                     width > this.imageEl.OriginWidth || 
27832                     height > this.imageEl.OriginHeight ||
27833                     (width < this.minWidth && height < this.minHeight)
27834                 )
27835         ){
27836             return false;
27837         }
27838         
27839         if(
27840                 this.isDocument &&
27841                 (this.rotate == 90 || this.rotate == 270) && 
27842                 (
27843                     width > this.imageEl.OriginWidth || 
27844                     height > this.imageEl.OriginHeight ||
27845                     (width < this.minHeight && height < this.minWidth)
27846                 )
27847         ){
27848             return false;
27849         }
27850         
27851         if(
27852                 !this.isDocument &&
27853                 (this.rotate == 0 || this.rotate == 180) && 
27854                 (
27855                     width < this.minWidth || 
27856                     width > this.imageEl.OriginWidth || 
27857                     height < this.minHeight || 
27858                     height > this.imageEl.OriginHeight
27859                 )
27860         ){
27861             return false;
27862         }
27863         
27864         if(
27865                 !this.isDocument &&
27866                 (this.rotate == 90 || this.rotate == 270) && 
27867                 (
27868                     width < this.minHeight || 
27869                     width > this.imageEl.OriginWidth || 
27870                     height < this.minWidth || 
27871                     height > this.imageEl.OriginHeight
27872                 )
27873         ){
27874             return false;
27875         }
27876         
27877         return true;
27878         
27879     },
27880     
27881     onRotateLeft : function(e)
27882     {   
27883         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27884             
27885             var minScale = this.thumbEl.getWidth() / this.minWidth;
27886             
27887             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27888             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27889             
27890             this.startScale = this.scale;
27891             
27892             while (this.getScaleLevel() < minScale){
27893             
27894                 this.scale = this.scale + 1;
27895                 
27896                 if(!this.zoomable()){
27897                     break;
27898                 }
27899                 
27900                 if(
27901                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27902                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27903                 ){
27904                     continue;
27905                 }
27906                 
27907                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27908
27909                 this.draw();
27910                 
27911                 return;
27912             }
27913             
27914             this.scale = this.startScale;
27915             
27916             this.onRotateFail();
27917             
27918             return false;
27919         }
27920         
27921         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27922
27923         if(this.isDocument){
27924             this.setThumbBoxSize();
27925             this.setThumbBoxPosition();
27926             this.setCanvasPosition();
27927         }
27928         
27929         this.draw();
27930         
27931         this.fireEvent('rotate', this, 'left');
27932         
27933     },
27934     
27935     onRotateRight : function(e)
27936     {
27937         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27938             
27939             var minScale = this.thumbEl.getWidth() / this.minWidth;
27940         
27941             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27942             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27943             
27944             this.startScale = this.scale;
27945             
27946             while (this.getScaleLevel() < minScale){
27947             
27948                 this.scale = this.scale + 1;
27949                 
27950                 if(!this.zoomable()){
27951                     break;
27952                 }
27953                 
27954                 if(
27955                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27956                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27957                 ){
27958                     continue;
27959                 }
27960                 
27961                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27962
27963                 this.draw();
27964                 
27965                 return;
27966             }
27967             
27968             this.scale = this.startScale;
27969             
27970             this.onRotateFail();
27971             
27972             return false;
27973         }
27974         
27975         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27976
27977         if(this.isDocument){
27978             this.setThumbBoxSize();
27979             this.setThumbBoxPosition();
27980             this.setCanvasPosition();
27981         }
27982         
27983         this.draw();
27984         
27985         this.fireEvent('rotate', this, 'right');
27986     },
27987     
27988     onRotateFail : function()
27989     {
27990         this.errorEl.show(true);
27991         
27992         var _this = this;
27993         
27994         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27995     },
27996     
27997     draw : function()
27998     {
27999         this.previewEl.dom.innerHTML = '';
28000         
28001         var canvasEl = document.createElement("canvas");
28002         
28003         var contextEl = canvasEl.getContext("2d");
28004         
28005         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28006         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28007         var center = this.imageEl.OriginWidth / 2;
28008         
28009         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
28010             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28011             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28012             center = this.imageEl.OriginHeight / 2;
28013         }
28014         
28015         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
28016         
28017         contextEl.translate(center, center);
28018         contextEl.rotate(this.rotate * Math.PI / 180);
28019
28020         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28021         
28022         this.canvasEl = document.createElement("canvas");
28023         
28024         this.contextEl = this.canvasEl.getContext("2d");
28025         
28026         switch (this.rotate) {
28027             case 0 :
28028                 
28029                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28030                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28031                 
28032                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28033                 
28034                 break;
28035             case 90 : 
28036                 
28037                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28038                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28039                 
28040                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28041                     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);
28042                     break;
28043                 }
28044                 
28045                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28046                 
28047                 break;
28048             case 180 :
28049                 
28050                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
28051                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
28052                 
28053                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28054                     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);
28055                     break;
28056                 }
28057                 
28058                 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);
28059                 
28060                 break;
28061             case 270 :
28062                 
28063                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
28064                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
28065         
28066                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28067                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
28068                     break;
28069                 }
28070                 
28071                 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);
28072                 
28073                 break;
28074             default : 
28075                 break;
28076         }
28077         
28078         this.previewEl.appendChild(this.canvasEl);
28079         
28080         this.setCanvasPosition();
28081     },
28082     
28083     crop : function()
28084     {
28085         if(!this.canvasLoaded){
28086             return;
28087         }
28088         
28089         var imageCanvas = document.createElement("canvas");
28090         
28091         var imageContext = imageCanvas.getContext("2d");
28092         
28093         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28094         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
28095         
28096         var center = imageCanvas.width / 2;
28097         
28098         imageContext.translate(center, center);
28099         
28100         imageContext.rotate(this.rotate * Math.PI / 180);
28101         
28102         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
28103         
28104         var canvas = document.createElement("canvas");
28105         
28106         var context = canvas.getContext("2d");
28107                 
28108         canvas.width = this.minWidth;
28109         canvas.height = this.minHeight;
28110
28111         switch (this.rotate) {
28112             case 0 :
28113                 
28114                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28115                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28116                 
28117                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28118                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28119                 
28120                 var targetWidth = this.minWidth - 2 * x;
28121                 var targetHeight = this.minHeight - 2 * y;
28122                 
28123                 var scale = 1;
28124                 
28125                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28126                     scale = targetWidth / width;
28127                 }
28128                 
28129                 if(x > 0 && y == 0){
28130                     scale = targetHeight / height;
28131                 }
28132                 
28133                 if(x > 0 && y > 0){
28134                     scale = targetWidth / width;
28135                     
28136                     if(width < height){
28137                         scale = targetHeight / height;
28138                     }
28139                 }
28140                 
28141                 context.scale(scale, scale);
28142                 
28143                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28144                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28145
28146                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28147                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28148
28149                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28150                 
28151                 break;
28152             case 90 : 
28153                 
28154                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28155                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (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                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28190                 
28191                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28192                 
28193                 break;
28194             case 180 :
28195                 
28196                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
28197                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
28198                 
28199                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28200                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28201                 
28202                 var targetWidth = this.minWidth - 2 * x;
28203                 var targetHeight = this.minHeight - 2 * y;
28204                 
28205                 var scale = 1;
28206                 
28207                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28208                     scale = targetWidth / width;
28209                 }
28210                 
28211                 if(x > 0 && y == 0){
28212                     scale = targetHeight / height;
28213                 }
28214                 
28215                 if(x > 0 && y > 0){
28216                     scale = targetWidth / width;
28217                     
28218                     if(width < height){
28219                         scale = targetHeight / height;
28220                     }
28221                 }
28222                 
28223                 context.scale(scale, scale);
28224                 
28225                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28226                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28227
28228                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28229                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28230
28231                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28232                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
28233                 
28234                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28235                 
28236                 break;
28237             case 270 :
28238                 
28239                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
28240                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
28241                 
28242                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
28243                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
28244                 
28245                 var targetWidth = this.minWidth - 2 * x;
28246                 var targetHeight = this.minHeight - 2 * y;
28247                 
28248                 var scale = 1;
28249                 
28250                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
28251                     scale = targetWidth / width;
28252                 }
28253                 
28254                 if(x > 0 && y == 0){
28255                     scale = targetHeight / height;
28256                 }
28257                 
28258                 if(x > 0 && y > 0){
28259                     scale = targetWidth / width;
28260                     
28261                     if(width < height){
28262                         scale = targetHeight / height;
28263                     }
28264                 }
28265                 
28266                 context.scale(scale, scale);
28267                 
28268                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
28269                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
28270
28271                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
28272                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
28273                 
28274                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
28275                 
28276                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
28277                 
28278                 break;
28279             default : 
28280                 break;
28281         }
28282         
28283         this.cropData = canvas.toDataURL(this.cropType);
28284         
28285         if(this.fireEvent('crop', this, this.cropData) !== false){
28286             this.process(this.file, this.cropData);
28287         }
28288         
28289         return;
28290         
28291     },
28292     
28293     setThumbBoxSize : function()
28294     {
28295         var width, height;
28296         
28297         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
28298             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
28299             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
28300             
28301             this.minWidth = width;
28302             this.minHeight = height;
28303             
28304             if(this.rotate == 90 || this.rotate == 270){
28305                 this.minWidth = height;
28306                 this.minHeight = width;
28307             }
28308         }
28309         
28310         height = 300;
28311         width = Math.ceil(this.minWidth * height / this.minHeight);
28312         
28313         if(this.minWidth > this.minHeight){
28314             width = 300;
28315             height = Math.ceil(this.minHeight * width / this.minWidth);
28316         }
28317         
28318         this.thumbEl.setStyle({
28319             width : width + 'px',
28320             height : height + 'px'
28321         });
28322
28323         return;
28324             
28325     },
28326     
28327     setThumbBoxPosition : function()
28328     {
28329         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
28330         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
28331         
28332         this.thumbEl.setLeft(x);
28333         this.thumbEl.setTop(y);
28334         
28335     },
28336     
28337     baseRotateLevel : function()
28338     {
28339         this.baseRotate = 1;
28340         
28341         if(
28342                 typeof(this.exif) != 'undefined' &&
28343                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
28344                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
28345         ){
28346             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
28347         }
28348         
28349         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
28350         
28351     },
28352     
28353     baseScaleLevel : function()
28354     {
28355         var width, height;
28356         
28357         if(this.isDocument){
28358             
28359             if(this.baseRotate == 6 || this.baseRotate == 8){
28360             
28361                 height = this.thumbEl.getHeight();
28362                 this.baseScale = height / this.imageEl.OriginWidth;
28363
28364                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
28365                     width = this.thumbEl.getWidth();
28366                     this.baseScale = width / this.imageEl.OriginHeight;
28367                 }
28368
28369                 return;
28370             }
28371
28372             height = this.thumbEl.getHeight();
28373             this.baseScale = height / this.imageEl.OriginHeight;
28374
28375             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
28376                 width = this.thumbEl.getWidth();
28377                 this.baseScale = width / this.imageEl.OriginWidth;
28378             }
28379
28380             return;
28381         }
28382         
28383         if(this.baseRotate == 6 || this.baseRotate == 8){
28384             
28385             width = this.thumbEl.getHeight();
28386             this.baseScale = width / this.imageEl.OriginHeight;
28387             
28388             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
28389                 height = this.thumbEl.getWidth();
28390                 this.baseScale = height / this.imageEl.OriginHeight;
28391             }
28392             
28393             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28394                 height = this.thumbEl.getWidth();
28395                 this.baseScale = height / this.imageEl.OriginHeight;
28396                 
28397                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
28398                     width = this.thumbEl.getHeight();
28399                     this.baseScale = width / this.imageEl.OriginWidth;
28400                 }
28401             }
28402             
28403             return;
28404         }
28405         
28406         width = this.thumbEl.getWidth();
28407         this.baseScale = width / this.imageEl.OriginWidth;
28408         
28409         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
28410             height = this.thumbEl.getHeight();
28411             this.baseScale = height / this.imageEl.OriginHeight;
28412         }
28413         
28414         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
28415             
28416             height = this.thumbEl.getHeight();
28417             this.baseScale = height / this.imageEl.OriginHeight;
28418             
28419             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
28420                 width = this.thumbEl.getWidth();
28421                 this.baseScale = width / this.imageEl.OriginWidth;
28422             }
28423             
28424         }
28425         
28426         return;
28427     },
28428     
28429     getScaleLevel : function()
28430     {
28431         return this.baseScale * Math.pow(1.1, this.scale);
28432     },
28433     
28434     onTouchStart : function(e)
28435     {
28436         if(!this.canvasLoaded){
28437             this.beforeSelectFile(e);
28438             return;
28439         }
28440         
28441         var touches = e.browserEvent.touches;
28442         
28443         if(!touches){
28444             return;
28445         }
28446         
28447         if(touches.length == 1){
28448             this.onMouseDown(e);
28449             return;
28450         }
28451         
28452         if(touches.length != 2){
28453             return;
28454         }
28455         
28456         var coords = [];
28457         
28458         for(var i = 0, finger; finger = touches[i]; i++){
28459             coords.push(finger.pageX, finger.pageY);
28460         }
28461         
28462         var x = Math.pow(coords[0] - coords[2], 2);
28463         var y = Math.pow(coords[1] - coords[3], 2);
28464         
28465         this.startDistance = Math.sqrt(x + y);
28466         
28467         this.startScale = this.scale;
28468         
28469         this.pinching = true;
28470         this.dragable = false;
28471         
28472     },
28473     
28474     onTouchMove : function(e)
28475     {
28476         if(!this.pinching && !this.dragable){
28477             return;
28478         }
28479         
28480         var touches = e.browserEvent.touches;
28481         
28482         if(!touches){
28483             return;
28484         }
28485         
28486         if(this.dragable){
28487             this.onMouseMove(e);
28488             return;
28489         }
28490         
28491         var coords = [];
28492         
28493         for(var i = 0, finger; finger = touches[i]; i++){
28494             coords.push(finger.pageX, finger.pageY);
28495         }
28496         
28497         var x = Math.pow(coords[0] - coords[2], 2);
28498         var y = Math.pow(coords[1] - coords[3], 2);
28499         
28500         this.endDistance = Math.sqrt(x + y);
28501         
28502         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
28503         
28504         if(!this.zoomable()){
28505             this.scale = this.startScale;
28506             return;
28507         }
28508         
28509         this.draw();
28510         
28511     },
28512     
28513     onTouchEnd : function(e)
28514     {
28515         this.pinching = false;
28516         this.dragable = false;
28517         
28518     },
28519     
28520     process : function(file, crop)
28521     {
28522         if(this.loadMask){
28523             this.maskEl.mask(this.loadingText);
28524         }
28525         
28526         this.xhr = new XMLHttpRequest();
28527         
28528         file.xhr = this.xhr;
28529
28530         this.xhr.open(this.method, this.url, true);
28531         
28532         var headers = {
28533             "Accept": "application/json",
28534             "Cache-Control": "no-cache",
28535             "X-Requested-With": "XMLHttpRequest"
28536         };
28537         
28538         for (var headerName in headers) {
28539             var headerValue = headers[headerName];
28540             if (headerValue) {
28541                 this.xhr.setRequestHeader(headerName, headerValue);
28542             }
28543         }
28544         
28545         var _this = this;
28546         
28547         this.xhr.onload = function()
28548         {
28549             _this.xhrOnLoad(_this.xhr);
28550         }
28551         
28552         this.xhr.onerror = function()
28553         {
28554             _this.xhrOnError(_this.xhr);
28555         }
28556         
28557         var formData = new FormData();
28558
28559         formData.append('returnHTML', 'NO');
28560         
28561         if(crop){
28562             formData.append('crop', crop);
28563         }
28564         
28565         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
28566             formData.append(this.paramName, file, file.name);
28567         }
28568         
28569         if(typeof(file.filename) != 'undefined'){
28570             formData.append('filename', file.filename);
28571         }
28572         
28573         if(typeof(file.mimetype) != 'undefined'){
28574             formData.append('mimetype', file.mimetype);
28575         }
28576         
28577         if(this.fireEvent('arrange', this, formData) != false){
28578             this.xhr.send(formData);
28579         };
28580     },
28581     
28582     xhrOnLoad : function(xhr)
28583     {
28584         if(this.loadMask){
28585             this.maskEl.unmask();
28586         }
28587         
28588         if (xhr.readyState !== 4) {
28589             this.fireEvent('exception', this, xhr);
28590             return;
28591         }
28592
28593         var response = Roo.decode(xhr.responseText);
28594         
28595         if(!response.success){
28596             this.fireEvent('exception', this, xhr);
28597             return;
28598         }
28599         
28600         var response = Roo.decode(xhr.responseText);
28601         
28602         this.fireEvent('upload', this, response);
28603         
28604     },
28605     
28606     xhrOnError : function()
28607     {
28608         if(this.loadMask){
28609             this.maskEl.unmask();
28610         }
28611         
28612         Roo.log('xhr on error');
28613         
28614         var response = Roo.decode(xhr.responseText);
28615           
28616         Roo.log(response);
28617         
28618     },
28619     
28620     prepare : function(file)
28621     {   
28622         if(this.loadMask){
28623             this.maskEl.mask(this.loadingText);
28624         }
28625         
28626         this.file = false;
28627         this.exif = {};
28628         
28629         if(typeof(file) === 'string'){
28630             this.loadCanvas(file);
28631             return;
28632         }
28633         
28634         if(!file || !this.urlAPI){
28635             return;
28636         }
28637         
28638         this.file = file;
28639         this.cropType = file.type;
28640         
28641         var _this = this;
28642         
28643         if(this.fireEvent('prepare', this, this.file) != false){
28644             
28645             var reader = new FileReader();
28646             
28647             reader.onload = function (e) {
28648                 if (e.target.error) {
28649                     Roo.log(e.target.error);
28650                     return;
28651                 }
28652                 
28653                 var buffer = e.target.result,
28654                     dataView = new DataView(buffer),
28655                     offset = 2,
28656                     maxOffset = dataView.byteLength - 4,
28657                     markerBytes,
28658                     markerLength;
28659                 
28660                 if (dataView.getUint16(0) === 0xffd8) {
28661                     while (offset < maxOffset) {
28662                         markerBytes = dataView.getUint16(offset);
28663                         
28664                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28665                             markerLength = dataView.getUint16(offset + 2) + 2;
28666                             if (offset + markerLength > dataView.byteLength) {
28667                                 Roo.log('Invalid meta data: Invalid segment size.');
28668                                 break;
28669                             }
28670                             
28671                             if(markerBytes == 0xffe1){
28672                                 _this.parseExifData(
28673                                     dataView,
28674                                     offset,
28675                                     markerLength
28676                                 );
28677                             }
28678                             
28679                             offset += markerLength;
28680                             
28681                             continue;
28682                         }
28683                         
28684                         break;
28685                     }
28686                     
28687                 }
28688                 
28689                 var url = _this.urlAPI.createObjectURL(_this.file);
28690                 
28691                 _this.loadCanvas(url);
28692                 
28693                 return;
28694             }
28695             
28696             reader.readAsArrayBuffer(this.file);
28697             
28698         }
28699         
28700     },
28701     
28702     parseExifData : function(dataView, offset, length)
28703     {
28704         var tiffOffset = offset + 10,
28705             littleEndian,
28706             dirOffset;
28707     
28708         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28709             // No Exif data, might be XMP data instead
28710             return;
28711         }
28712         
28713         // Check for the ASCII code for "Exif" (0x45786966):
28714         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28715             // No Exif data, might be XMP data instead
28716             return;
28717         }
28718         if (tiffOffset + 8 > dataView.byteLength) {
28719             Roo.log('Invalid Exif data: Invalid segment size.');
28720             return;
28721         }
28722         // Check for the two null bytes:
28723         if (dataView.getUint16(offset + 8) !== 0x0000) {
28724             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28725             return;
28726         }
28727         // Check the byte alignment:
28728         switch (dataView.getUint16(tiffOffset)) {
28729         case 0x4949:
28730             littleEndian = true;
28731             break;
28732         case 0x4D4D:
28733             littleEndian = false;
28734             break;
28735         default:
28736             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28737             return;
28738         }
28739         // Check for the TIFF tag marker (0x002A):
28740         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28741             Roo.log('Invalid Exif data: Missing TIFF marker.');
28742             return;
28743         }
28744         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28745         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28746         
28747         this.parseExifTags(
28748             dataView,
28749             tiffOffset,
28750             tiffOffset + dirOffset,
28751             littleEndian
28752         );
28753     },
28754     
28755     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28756     {
28757         var tagsNumber,
28758             dirEndOffset,
28759             i;
28760         if (dirOffset + 6 > dataView.byteLength) {
28761             Roo.log('Invalid Exif data: Invalid directory offset.');
28762             return;
28763         }
28764         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28765         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28766         if (dirEndOffset + 4 > dataView.byteLength) {
28767             Roo.log('Invalid Exif data: Invalid directory size.');
28768             return;
28769         }
28770         for (i = 0; i < tagsNumber; i += 1) {
28771             this.parseExifTag(
28772                 dataView,
28773                 tiffOffset,
28774                 dirOffset + 2 + 12 * i, // tag offset
28775                 littleEndian
28776             );
28777         }
28778         // Return the offset to the next directory:
28779         return dataView.getUint32(dirEndOffset, littleEndian);
28780     },
28781     
28782     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28783     {
28784         var tag = dataView.getUint16(offset, littleEndian);
28785         
28786         this.exif[tag] = this.getExifValue(
28787             dataView,
28788             tiffOffset,
28789             offset,
28790             dataView.getUint16(offset + 2, littleEndian), // tag type
28791             dataView.getUint32(offset + 4, littleEndian), // tag length
28792             littleEndian
28793         );
28794     },
28795     
28796     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28797     {
28798         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28799             tagSize,
28800             dataOffset,
28801             values,
28802             i,
28803             str,
28804             c;
28805     
28806         if (!tagType) {
28807             Roo.log('Invalid Exif data: Invalid tag type.');
28808             return;
28809         }
28810         
28811         tagSize = tagType.size * length;
28812         // Determine if the value is contained in the dataOffset bytes,
28813         // or if the value at the dataOffset is a pointer to the actual data:
28814         dataOffset = tagSize > 4 ?
28815                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28816         if (dataOffset + tagSize > dataView.byteLength) {
28817             Roo.log('Invalid Exif data: Invalid data offset.');
28818             return;
28819         }
28820         if (length === 1) {
28821             return tagType.getValue(dataView, dataOffset, littleEndian);
28822         }
28823         values = [];
28824         for (i = 0; i < length; i += 1) {
28825             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28826         }
28827         
28828         if (tagType.ascii) {
28829             str = '';
28830             // Concatenate the chars:
28831             for (i = 0; i < values.length; i += 1) {
28832                 c = values[i];
28833                 // Ignore the terminating NULL byte(s):
28834                 if (c === '\u0000') {
28835                     break;
28836                 }
28837                 str += c;
28838             }
28839             return str;
28840         }
28841         return values;
28842     }
28843     
28844 });
28845
28846 Roo.apply(Roo.bootstrap.UploadCropbox, {
28847     tags : {
28848         'Orientation': 0x0112
28849     },
28850     
28851     Orientation: {
28852             1: 0, //'top-left',
28853 //            2: 'top-right',
28854             3: 180, //'bottom-right',
28855 //            4: 'bottom-left',
28856 //            5: 'left-top',
28857             6: 90, //'right-top',
28858 //            7: 'right-bottom',
28859             8: 270 //'left-bottom'
28860     },
28861     
28862     exifTagTypes : {
28863         // byte, 8-bit unsigned int:
28864         1: {
28865             getValue: function (dataView, dataOffset) {
28866                 return dataView.getUint8(dataOffset);
28867             },
28868             size: 1
28869         },
28870         // ascii, 8-bit byte:
28871         2: {
28872             getValue: function (dataView, dataOffset) {
28873                 return String.fromCharCode(dataView.getUint8(dataOffset));
28874             },
28875             size: 1,
28876             ascii: true
28877         },
28878         // short, 16 bit int:
28879         3: {
28880             getValue: function (dataView, dataOffset, littleEndian) {
28881                 return dataView.getUint16(dataOffset, littleEndian);
28882             },
28883             size: 2
28884         },
28885         // long, 32 bit int:
28886         4: {
28887             getValue: function (dataView, dataOffset, littleEndian) {
28888                 return dataView.getUint32(dataOffset, littleEndian);
28889             },
28890             size: 4
28891         },
28892         // rational = two long values, first is numerator, second is denominator:
28893         5: {
28894             getValue: function (dataView, dataOffset, littleEndian) {
28895                 return dataView.getUint32(dataOffset, littleEndian) /
28896                     dataView.getUint32(dataOffset + 4, littleEndian);
28897             },
28898             size: 8
28899         },
28900         // slong, 32 bit signed int:
28901         9: {
28902             getValue: function (dataView, dataOffset, littleEndian) {
28903                 return dataView.getInt32(dataOffset, littleEndian);
28904             },
28905             size: 4
28906         },
28907         // srational, two slongs, first is numerator, second is denominator:
28908         10: {
28909             getValue: function (dataView, dataOffset, littleEndian) {
28910                 return dataView.getInt32(dataOffset, littleEndian) /
28911                     dataView.getInt32(dataOffset + 4, littleEndian);
28912             },
28913             size: 8
28914         }
28915     },
28916     
28917     footer : {
28918         STANDARD : [
28919             {
28920                 tag : 'div',
28921                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28922                 action : 'rotate-left',
28923                 cn : [
28924                     {
28925                         tag : 'button',
28926                         cls : 'btn btn-default',
28927                         html : '<i class="fa fa-undo"></i>'
28928                     }
28929                 ]
28930             },
28931             {
28932                 tag : 'div',
28933                 cls : 'btn-group roo-upload-cropbox-picture',
28934                 action : 'picture',
28935                 cn : [
28936                     {
28937                         tag : 'button',
28938                         cls : 'btn btn-default',
28939                         html : '<i class="fa fa-picture-o"></i>'
28940                     }
28941                 ]
28942             },
28943             {
28944                 tag : 'div',
28945                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28946                 action : 'rotate-right',
28947                 cn : [
28948                     {
28949                         tag : 'button',
28950                         cls : 'btn btn-default',
28951                         html : '<i class="fa fa-repeat"></i>'
28952                     }
28953                 ]
28954             }
28955         ],
28956         DOCUMENT : [
28957             {
28958                 tag : 'div',
28959                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28960                 action : 'rotate-left',
28961                 cn : [
28962                     {
28963                         tag : 'button',
28964                         cls : 'btn btn-default',
28965                         html : '<i class="fa fa-undo"></i>'
28966                     }
28967                 ]
28968             },
28969             {
28970                 tag : 'div',
28971                 cls : 'btn-group roo-upload-cropbox-download',
28972                 action : 'download',
28973                 cn : [
28974                     {
28975                         tag : 'button',
28976                         cls : 'btn btn-default',
28977                         html : '<i class="fa fa-download"></i>'
28978                     }
28979                 ]
28980             },
28981             {
28982                 tag : 'div',
28983                 cls : 'btn-group roo-upload-cropbox-crop',
28984                 action : 'crop',
28985                 cn : [
28986                     {
28987                         tag : 'button',
28988                         cls : 'btn btn-default',
28989                         html : '<i class="fa fa-crop"></i>'
28990                     }
28991                 ]
28992             },
28993             {
28994                 tag : 'div',
28995                 cls : 'btn-group roo-upload-cropbox-trash',
28996                 action : 'trash',
28997                 cn : [
28998                     {
28999                         tag : 'button',
29000                         cls : 'btn btn-default',
29001                         html : '<i class="fa fa-trash"></i>'
29002                     }
29003                 ]
29004             },
29005             {
29006                 tag : 'div',
29007                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29008                 action : 'rotate-right',
29009                 cn : [
29010                     {
29011                         tag : 'button',
29012                         cls : 'btn btn-default',
29013                         html : '<i class="fa fa-repeat"></i>'
29014                     }
29015                 ]
29016             }
29017         ],
29018         ROTATOR : [
29019             {
29020                 tag : 'div',
29021                 cls : 'btn-group roo-upload-cropbox-rotate-left',
29022                 action : 'rotate-left',
29023                 cn : [
29024                     {
29025                         tag : 'button',
29026                         cls : 'btn btn-default',
29027                         html : '<i class="fa fa-undo"></i>'
29028                     }
29029                 ]
29030             },
29031             {
29032                 tag : 'div',
29033                 cls : 'btn-group roo-upload-cropbox-rotate-right',
29034                 action : 'rotate-right',
29035                 cn : [
29036                     {
29037                         tag : 'button',
29038                         cls : 'btn btn-default',
29039                         html : '<i class="fa fa-repeat"></i>'
29040                     }
29041                 ]
29042             }
29043         ]
29044     }
29045 });
29046
29047 /*
29048 * Licence: LGPL
29049 */
29050
29051 /**
29052  * @class Roo.bootstrap.DocumentManager
29053  * @extends Roo.bootstrap.Component
29054  * Bootstrap DocumentManager class
29055  * @cfg {String} paramName default 'imageUpload'
29056  * @cfg {String} toolTipName default 'filename'
29057  * @cfg {String} method default POST
29058  * @cfg {String} url action url
29059  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
29060  * @cfg {Boolean} multiple multiple upload default true
29061  * @cfg {Number} thumbSize default 300
29062  * @cfg {String} fieldLabel
29063  * @cfg {Number} labelWidth default 4
29064  * @cfg {String} labelAlign (left|top) default left
29065  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
29066 * @cfg {Number} labellg set the width of label (1-12)
29067  * @cfg {Number} labelmd set the width of label (1-12)
29068  * @cfg {Number} labelsm set the width of label (1-12)
29069  * @cfg {Number} labelxs set the width of label (1-12)
29070  * 
29071  * @constructor
29072  * Create a new DocumentManager
29073  * @param {Object} config The config object
29074  */
29075
29076 Roo.bootstrap.DocumentManager = function(config){
29077     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
29078     
29079     this.files = [];
29080     this.delegates = [];
29081     
29082     this.addEvents({
29083         /**
29084          * @event initial
29085          * Fire when initial the DocumentManager
29086          * @param {Roo.bootstrap.DocumentManager} this
29087          */
29088         "initial" : true,
29089         /**
29090          * @event inspect
29091          * inspect selected file
29092          * @param {Roo.bootstrap.DocumentManager} this
29093          * @param {File} file
29094          */
29095         "inspect" : true,
29096         /**
29097          * @event exception
29098          * Fire when xhr load exception
29099          * @param {Roo.bootstrap.DocumentManager} this
29100          * @param {XMLHttpRequest} xhr
29101          */
29102         "exception" : true,
29103         /**
29104          * @event afterupload
29105          * Fire when xhr load exception
29106          * @param {Roo.bootstrap.DocumentManager} this
29107          * @param {XMLHttpRequest} xhr
29108          */
29109         "afterupload" : true,
29110         /**
29111          * @event prepare
29112          * prepare the form data
29113          * @param {Roo.bootstrap.DocumentManager} this
29114          * @param {Object} formData
29115          */
29116         "prepare" : true,
29117         /**
29118          * @event remove
29119          * Fire when remove the file
29120          * @param {Roo.bootstrap.DocumentManager} this
29121          * @param {Object} file
29122          */
29123         "remove" : true,
29124         /**
29125          * @event refresh
29126          * Fire after refresh the file
29127          * @param {Roo.bootstrap.DocumentManager} this
29128          */
29129         "refresh" : true,
29130         /**
29131          * @event click
29132          * Fire after click the image
29133          * @param {Roo.bootstrap.DocumentManager} this
29134          * @param {Object} file
29135          */
29136         "click" : true,
29137         /**
29138          * @event edit
29139          * Fire when upload a image and editable set to true
29140          * @param {Roo.bootstrap.DocumentManager} this
29141          * @param {Object} file
29142          */
29143         "edit" : true,
29144         /**
29145          * @event beforeselectfile
29146          * Fire before select file
29147          * @param {Roo.bootstrap.DocumentManager} this
29148          */
29149         "beforeselectfile" : true,
29150         /**
29151          * @event process
29152          * Fire before process file
29153          * @param {Roo.bootstrap.DocumentManager} this
29154          * @param {Object} file
29155          */
29156         "process" : true,
29157         /**
29158          * @event previewrendered
29159          * Fire when preview rendered
29160          * @param {Roo.bootstrap.DocumentManager} this
29161          * @param {Object} file
29162          */
29163         "previewrendered" : true,
29164         /**
29165          */
29166         "previewResize" : true
29167         
29168     });
29169 };
29170
29171 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
29172     
29173     boxes : 0,
29174     inputName : '',
29175     thumbSize : 300,
29176     multiple : true,
29177     files : false,
29178     method : 'POST',
29179     url : '',
29180     paramName : 'imageUpload',
29181     toolTipName : 'filename',
29182     fieldLabel : '',
29183     labelWidth : 4,
29184     labelAlign : 'left',
29185     editable : true,
29186     delegates : false,
29187     xhr : false, 
29188     
29189     labellg : 0,
29190     labelmd : 0,
29191     labelsm : 0,
29192     labelxs : 0,
29193     
29194     getAutoCreate : function()
29195     {   
29196         var managerWidget = {
29197             tag : 'div',
29198             cls : 'roo-document-manager',
29199             cn : [
29200                 {
29201                     tag : 'input',
29202                     cls : 'roo-document-manager-selector',
29203                     type : 'file'
29204                 },
29205                 {
29206                     tag : 'div',
29207                     cls : 'roo-document-manager-uploader',
29208                     cn : [
29209                         {
29210                             tag : 'div',
29211                             cls : 'roo-document-manager-upload-btn',
29212                             html : '<i class="fa fa-plus"></i>'
29213                         }
29214                     ]
29215                     
29216                 }
29217             ]
29218         };
29219         
29220         var content = [
29221             {
29222                 tag : 'div',
29223                 cls : 'column col-md-12',
29224                 cn : managerWidget
29225             }
29226         ];
29227         
29228         if(this.fieldLabel.length){
29229             
29230             content = [
29231                 {
29232                     tag : 'div',
29233                     cls : 'column col-md-12',
29234                     html : this.fieldLabel
29235                 },
29236                 {
29237                     tag : 'div',
29238                     cls : 'column col-md-12',
29239                     cn : managerWidget
29240                 }
29241             ];
29242
29243             if(this.labelAlign == 'left'){
29244                 content = [
29245                     {
29246                         tag : 'div',
29247                         cls : 'column',
29248                         html : this.fieldLabel
29249                     },
29250                     {
29251                         tag : 'div',
29252                         cls : 'column',
29253                         cn : managerWidget
29254                     }
29255                 ];
29256                 
29257                 if(this.labelWidth > 12){
29258                     content[0].style = "width: " + this.labelWidth + 'px';
29259                 }
29260
29261                 if(this.labelWidth < 13 && this.labelmd == 0){
29262                     this.labelmd = this.labelWidth;
29263                 }
29264
29265                 if(this.labellg > 0){
29266                     content[0].cls += ' col-lg-' + this.labellg;
29267                     content[1].cls += ' col-lg-' + (12 - this.labellg);
29268                 }
29269
29270                 if(this.labelmd > 0){
29271                     content[0].cls += ' col-md-' + this.labelmd;
29272                     content[1].cls += ' col-md-' + (12 - this.labelmd);
29273                 }
29274
29275                 if(this.labelsm > 0){
29276                     content[0].cls += ' col-sm-' + this.labelsm;
29277                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
29278                 }
29279
29280                 if(this.labelxs > 0){
29281                     content[0].cls += ' col-xs-' + this.labelxs;
29282                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
29283                 }
29284                 
29285             }
29286         }
29287         
29288         var cfg = {
29289             tag : 'div',
29290             cls : 'row clearfix',
29291             cn : content
29292         };
29293         
29294         return cfg;
29295         
29296     },
29297     
29298     initEvents : function()
29299     {
29300         this.managerEl = this.el.select('.roo-document-manager', true).first();
29301         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29302         
29303         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
29304         this.selectorEl.hide();
29305         
29306         if(this.multiple){
29307             this.selectorEl.attr('multiple', 'multiple');
29308         }
29309         
29310         this.selectorEl.on('change', this.onFileSelected, this);
29311         
29312         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
29313         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29314         
29315         this.uploader.on('click', this.onUploaderClick, this);
29316         
29317         this.renderProgressDialog();
29318         
29319         var _this = this;
29320         
29321         window.addEventListener("resize", function() { _this.refresh(); } );
29322         
29323         this.fireEvent('initial', this);
29324     },
29325     
29326     renderProgressDialog : function()
29327     {
29328         var _this = this;
29329         
29330         this.progressDialog = new Roo.bootstrap.Modal({
29331             cls : 'roo-document-manager-progress-dialog',
29332             allow_close : false,
29333             animate : false,
29334             title : '',
29335             buttons : [
29336                 {
29337                     name  :'cancel',
29338                     weight : 'danger',
29339                     html : 'Cancel'
29340                 }
29341             ], 
29342             listeners : { 
29343                 btnclick : function() {
29344                     _this.uploadCancel();
29345                     this.hide();
29346                 }
29347             }
29348         });
29349          
29350         this.progressDialog.render(Roo.get(document.body));
29351          
29352         this.progress = new Roo.bootstrap.Progress({
29353             cls : 'roo-document-manager-progress',
29354             active : true,
29355             striped : true
29356         });
29357         
29358         this.progress.render(this.progressDialog.getChildContainer());
29359         
29360         this.progressBar = new Roo.bootstrap.ProgressBar({
29361             cls : 'roo-document-manager-progress-bar',
29362             aria_valuenow : 0,
29363             aria_valuemin : 0,
29364             aria_valuemax : 12,
29365             panel : 'success'
29366         });
29367         
29368         this.progressBar.render(this.progress.getChildContainer());
29369     },
29370     
29371     onUploaderClick : function(e)
29372     {
29373         e.preventDefault();
29374      
29375         if(this.fireEvent('beforeselectfile', this) != false){
29376             this.selectorEl.dom.click();
29377         }
29378         
29379     },
29380     
29381     onFileSelected : function(e)
29382     {
29383         e.preventDefault();
29384         
29385         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29386             return;
29387         }
29388         
29389         Roo.each(this.selectorEl.dom.files, function(file){
29390             if(this.fireEvent('inspect', this, file) != false){
29391                 this.files.push(file);
29392             }
29393         }, this);
29394         
29395         this.queue();
29396         
29397     },
29398     
29399     queue : function()
29400     {
29401         this.selectorEl.dom.value = '';
29402         
29403         if(!this.files || !this.files.length){
29404             return;
29405         }
29406         
29407         if(this.boxes > 0 && this.files.length > this.boxes){
29408             this.files = this.files.slice(0, this.boxes);
29409         }
29410         
29411         this.uploader.show();
29412         
29413         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29414             this.uploader.hide();
29415         }
29416         
29417         var _this = this;
29418         
29419         var files = [];
29420         
29421         var docs = [];
29422         
29423         Roo.each(this.files, function(file){
29424             
29425             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29426                 var f = this.renderPreview(file);
29427                 files.push(f);
29428                 return;
29429             }
29430             
29431             if(file.type.indexOf('image') != -1){
29432                 this.delegates.push(
29433                     (function(){
29434                         _this.process(file);
29435                     }).createDelegate(this)
29436                 );
29437         
29438                 return;
29439             }
29440             
29441             docs.push(
29442                 (function(){
29443                     _this.process(file);
29444                 }).createDelegate(this)
29445             );
29446             
29447         }, this);
29448         
29449         this.files = files;
29450         
29451         this.delegates = this.delegates.concat(docs);
29452         
29453         if(!this.delegates.length){
29454             this.refresh();
29455             return;
29456         }
29457         
29458         this.progressBar.aria_valuemax = this.delegates.length;
29459         
29460         this.arrange();
29461         
29462         return;
29463     },
29464     
29465     arrange : function()
29466     {
29467         if(!this.delegates.length){
29468             this.progressDialog.hide();
29469             this.refresh();
29470             return;
29471         }
29472         
29473         var delegate = this.delegates.shift();
29474         
29475         this.progressDialog.show();
29476         
29477         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
29478         
29479         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
29480         
29481         delegate();
29482     },
29483     
29484     refresh : function()
29485     {
29486         this.uploader.show();
29487         
29488         if(this.boxes > 0 && this.files.length > this.boxes - 1){
29489             this.uploader.hide();
29490         }
29491         
29492         Roo.isTouch ? this.closable(false) : this.closable(true);
29493         
29494         this.fireEvent('refresh', this);
29495     },
29496     
29497     onRemove : function(e, el, o)
29498     {
29499         e.preventDefault();
29500         
29501         this.fireEvent('remove', this, o);
29502         
29503     },
29504     
29505     remove : function(o)
29506     {
29507         var files = [];
29508         
29509         Roo.each(this.files, function(file){
29510             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
29511                 files.push(file);
29512                 return;
29513             }
29514
29515             o.target.remove();
29516
29517         }, this);
29518         
29519         this.files = files;
29520         
29521         this.refresh();
29522     },
29523     
29524     clear : function()
29525     {
29526         Roo.each(this.files, function(file){
29527             if(!file.target){
29528                 return;
29529             }
29530             
29531             file.target.remove();
29532
29533         }, this);
29534         
29535         this.files = [];
29536         
29537         this.refresh();
29538     },
29539     
29540     onClick : function(e, el, o)
29541     {
29542         e.preventDefault();
29543         
29544         this.fireEvent('click', this, o);
29545         
29546     },
29547     
29548     closable : function(closable)
29549     {
29550         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
29551             
29552             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29553             
29554             if(closable){
29555                 el.show();
29556                 return;
29557             }
29558             
29559             el.hide();
29560             
29561         }, this);
29562     },
29563     
29564     xhrOnLoad : function(xhr)
29565     {
29566         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29567             el.remove();
29568         }, this);
29569         
29570         if (xhr.readyState !== 4) {
29571             this.arrange();
29572             this.fireEvent('exception', this, xhr);
29573             return;
29574         }
29575
29576         var response = Roo.decode(xhr.responseText);
29577         
29578         if(!response.success){
29579             this.arrange();
29580             this.fireEvent('exception', this, xhr);
29581             return;
29582         }
29583         
29584         var file = this.renderPreview(response.data);
29585         
29586         this.files.push(file);
29587         
29588         this.arrange();
29589         
29590         this.fireEvent('afterupload', this, xhr);
29591         
29592     },
29593     
29594     xhrOnError : function(xhr)
29595     {
29596         Roo.log('xhr on error');
29597         
29598         var response = Roo.decode(xhr.responseText);
29599           
29600         Roo.log(response);
29601         
29602         this.arrange();
29603     },
29604     
29605     process : function(file)
29606     {
29607         if(this.fireEvent('process', this, file) !== false){
29608             if(this.editable && file.type.indexOf('image') != -1){
29609                 this.fireEvent('edit', this, file);
29610                 return;
29611             }
29612
29613             this.uploadStart(file, false);
29614
29615             return;
29616         }
29617         
29618     },
29619     
29620     uploadStart : function(file, crop)
29621     {
29622         this.xhr = new XMLHttpRequest();
29623         
29624         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
29625             this.arrange();
29626             return;
29627         }
29628         
29629         file.xhr = this.xhr;
29630             
29631         this.managerEl.createChild({
29632             tag : 'div',
29633             cls : 'roo-document-manager-loading',
29634             cn : [
29635                 {
29636                     tag : 'div',
29637                     tooltip : file.name,
29638                     cls : 'roo-document-manager-thumb',
29639                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29640                 }
29641             ]
29642
29643         });
29644
29645         this.xhr.open(this.method, this.url, true);
29646         
29647         var headers = {
29648             "Accept": "application/json",
29649             "Cache-Control": "no-cache",
29650             "X-Requested-With": "XMLHttpRequest"
29651         };
29652         
29653         for (var headerName in headers) {
29654             var headerValue = headers[headerName];
29655             if (headerValue) {
29656                 this.xhr.setRequestHeader(headerName, headerValue);
29657             }
29658         }
29659         
29660         var _this = this;
29661         
29662         this.xhr.onload = function()
29663         {
29664             _this.xhrOnLoad(_this.xhr);
29665         }
29666         
29667         this.xhr.onerror = function()
29668         {
29669             _this.xhrOnError(_this.xhr);
29670         }
29671         
29672         var formData = new FormData();
29673
29674         formData.append('returnHTML', 'NO');
29675         
29676         if(crop){
29677             formData.append('crop', crop);
29678         }
29679         
29680         formData.append(this.paramName, file, file.name);
29681         
29682         var options = {
29683             file : file, 
29684             manually : false
29685         };
29686         
29687         if(this.fireEvent('prepare', this, formData, options) != false){
29688             
29689             if(options.manually){
29690                 return;
29691             }
29692             
29693             this.xhr.send(formData);
29694             return;
29695         };
29696         
29697         this.uploadCancel();
29698     },
29699     
29700     uploadCancel : function()
29701     {
29702         if (this.xhr) {
29703             this.xhr.abort();
29704         }
29705         
29706         this.delegates = [];
29707         
29708         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29709             el.remove();
29710         }, this);
29711         
29712         this.arrange();
29713     },
29714     
29715     renderPreview : function(file)
29716     {
29717         if(typeof(file.target) != 'undefined' && file.target){
29718             return file;
29719         }
29720         
29721         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29722         
29723         var previewEl = this.managerEl.createChild({
29724             tag : 'div',
29725             cls : 'roo-document-manager-preview',
29726             cn : [
29727                 {
29728                     tag : 'div',
29729                     tooltip : file[this.toolTipName],
29730                     cls : 'roo-document-manager-thumb',
29731                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29732                 },
29733                 {
29734                     tag : 'button',
29735                     cls : 'close',
29736                     html : '<i class="fa fa-times-circle"></i>'
29737                 }
29738             ]
29739         });
29740
29741         var close = previewEl.select('button.close', true).first();
29742
29743         close.on('click', this.onRemove, this, file);
29744
29745         file.target = previewEl;
29746
29747         var image = previewEl.select('img', true).first();
29748         
29749         var _this = this;
29750         
29751         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29752         
29753         image.on('click', this.onClick, this, file);
29754         
29755         this.fireEvent('previewrendered', this, file);
29756         
29757         return file;
29758         
29759     },
29760     
29761     onPreviewLoad : function(file, image)
29762     {
29763         if(typeof(file.target) == 'undefined' || !file.target){
29764             return;
29765         }
29766         
29767         var width = image.dom.naturalWidth || image.dom.width;
29768         var height = image.dom.naturalHeight || image.dom.height;
29769         
29770         if(!this.previewResize) {
29771             return;
29772         }
29773         
29774         if(width > height){
29775             file.target.addClass('wide');
29776             return;
29777         }
29778         
29779         file.target.addClass('tall');
29780         return;
29781         
29782     },
29783     
29784     uploadFromSource : function(file, crop)
29785     {
29786         this.xhr = new XMLHttpRequest();
29787         
29788         this.managerEl.createChild({
29789             tag : 'div',
29790             cls : 'roo-document-manager-loading',
29791             cn : [
29792                 {
29793                     tag : 'div',
29794                     tooltip : file.name,
29795                     cls : 'roo-document-manager-thumb',
29796                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29797                 }
29798             ]
29799
29800         });
29801
29802         this.xhr.open(this.method, this.url, true);
29803         
29804         var headers = {
29805             "Accept": "application/json",
29806             "Cache-Control": "no-cache",
29807             "X-Requested-With": "XMLHttpRequest"
29808         };
29809         
29810         for (var headerName in headers) {
29811             var headerValue = headers[headerName];
29812             if (headerValue) {
29813                 this.xhr.setRequestHeader(headerName, headerValue);
29814             }
29815         }
29816         
29817         var _this = this;
29818         
29819         this.xhr.onload = function()
29820         {
29821             _this.xhrOnLoad(_this.xhr);
29822         }
29823         
29824         this.xhr.onerror = function()
29825         {
29826             _this.xhrOnError(_this.xhr);
29827         }
29828         
29829         var formData = new FormData();
29830
29831         formData.append('returnHTML', 'NO');
29832         
29833         formData.append('crop', crop);
29834         
29835         if(typeof(file.filename) != 'undefined'){
29836             formData.append('filename', file.filename);
29837         }
29838         
29839         if(typeof(file.mimetype) != 'undefined'){
29840             formData.append('mimetype', file.mimetype);
29841         }
29842         
29843         Roo.log(formData);
29844         
29845         if(this.fireEvent('prepare', this, formData) != false){
29846             this.xhr.send(formData);
29847         };
29848     }
29849 });
29850
29851 /*
29852 * Licence: LGPL
29853 */
29854
29855 /**
29856  * @class Roo.bootstrap.DocumentViewer
29857  * @extends Roo.bootstrap.Component
29858  * Bootstrap DocumentViewer class
29859  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29860  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29861  * 
29862  * @constructor
29863  * Create a new DocumentViewer
29864  * @param {Object} config The config object
29865  */
29866
29867 Roo.bootstrap.DocumentViewer = function(config){
29868     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29869     
29870     this.addEvents({
29871         /**
29872          * @event initial
29873          * Fire after initEvent
29874          * @param {Roo.bootstrap.DocumentViewer} this
29875          */
29876         "initial" : true,
29877         /**
29878          * @event click
29879          * Fire after click
29880          * @param {Roo.bootstrap.DocumentViewer} this
29881          */
29882         "click" : true,
29883         /**
29884          * @event download
29885          * Fire after download button
29886          * @param {Roo.bootstrap.DocumentViewer} this
29887          */
29888         "download" : true,
29889         /**
29890          * @event trash
29891          * Fire after trash button
29892          * @param {Roo.bootstrap.DocumentViewer} this
29893          */
29894         "trash" : true
29895         
29896     });
29897 };
29898
29899 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29900     
29901     showDownload : true,
29902     
29903     showTrash : true,
29904     
29905     getAutoCreate : function()
29906     {
29907         var cfg = {
29908             tag : 'div',
29909             cls : 'roo-document-viewer',
29910             cn : [
29911                 {
29912                     tag : 'div',
29913                     cls : 'roo-document-viewer-body',
29914                     cn : [
29915                         {
29916                             tag : 'div',
29917                             cls : 'roo-document-viewer-thumb',
29918                             cn : [
29919                                 {
29920                                     tag : 'img',
29921                                     cls : 'roo-document-viewer-image'
29922                                 }
29923                             ]
29924                         }
29925                     ]
29926                 },
29927                 {
29928                     tag : 'div',
29929                     cls : 'roo-document-viewer-footer',
29930                     cn : {
29931                         tag : 'div',
29932                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29933                         cn : [
29934                             {
29935                                 tag : 'div',
29936                                 cls : 'btn-group roo-document-viewer-download',
29937                                 cn : [
29938                                     {
29939                                         tag : 'button',
29940                                         cls : 'btn btn-default',
29941                                         html : '<i class="fa fa-download"></i>'
29942                                     }
29943                                 ]
29944                             },
29945                             {
29946                                 tag : 'div',
29947                                 cls : 'btn-group roo-document-viewer-trash',
29948                                 cn : [
29949                                     {
29950                                         tag : 'button',
29951                                         cls : 'btn btn-default',
29952                                         html : '<i class="fa fa-trash"></i>'
29953                                     }
29954                                 ]
29955                             }
29956                         ]
29957                     }
29958                 }
29959             ]
29960         };
29961         
29962         return cfg;
29963     },
29964     
29965     initEvents : function()
29966     {
29967         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29968         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29969         
29970         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29971         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29972         
29973         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29974         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29975         
29976         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29977         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29978         
29979         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29980         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29981         
29982         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29983         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29984         
29985         this.bodyEl.on('click', this.onClick, this);
29986         this.downloadBtn.on('click', this.onDownload, this);
29987         this.trashBtn.on('click', this.onTrash, this);
29988         
29989         this.downloadBtn.hide();
29990         this.trashBtn.hide();
29991         
29992         if(this.showDownload){
29993             this.downloadBtn.show();
29994         }
29995         
29996         if(this.showTrash){
29997             this.trashBtn.show();
29998         }
29999         
30000         if(!this.showDownload && !this.showTrash) {
30001             this.footerEl.hide();
30002         }
30003         
30004     },
30005     
30006     initial : function()
30007     {
30008         this.fireEvent('initial', this);
30009         
30010     },
30011     
30012     onClick : function(e)
30013     {
30014         e.preventDefault();
30015         
30016         this.fireEvent('click', this);
30017     },
30018     
30019     onDownload : function(e)
30020     {
30021         e.preventDefault();
30022         
30023         this.fireEvent('download', this);
30024     },
30025     
30026     onTrash : function(e)
30027     {
30028         e.preventDefault();
30029         
30030         this.fireEvent('trash', this);
30031     }
30032     
30033 });
30034 /*
30035  * - LGPL
30036  *
30037  * nav progress bar
30038  * 
30039  */
30040
30041 /**
30042  * @class Roo.bootstrap.NavProgressBar
30043  * @extends Roo.bootstrap.Component
30044  * Bootstrap NavProgressBar class
30045  * 
30046  * @constructor
30047  * Create a new nav progress bar
30048  * @param {Object} config The config object
30049  */
30050
30051 Roo.bootstrap.NavProgressBar = function(config){
30052     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
30053
30054     this.bullets = this.bullets || [];
30055    
30056 //    Roo.bootstrap.NavProgressBar.register(this);
30057      this.addEvents({
30058         /**
30059              * @event changed
30060              * Fires when the active item changes
30061              * @param {Roo.bootstrap.NavProgressBar} this
30062              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
30063              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
30064          */
30065         'changed': true
30066      });
30067     
30068 };
30069
30070 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
30071     
30072     bullets : [],
30073     barItems : [],
30074     
30075     getAutoCreate : function()
30076     {
30077         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
30078         
30079         cfg = {
30080             tag : 'div',
30081             cls : 'roo-navigation-bar-group',
30082             cn : [
30083                 {
30084                     tag : 'div',
30085                     cls : 'roo-navigation-top-bar'
30086                 },
30087                 {
30088                     tag : 'div',
30089                     cls : 'roo-navigation-bullets-bar',
30090                     cn : [
30091                         {
30092                             tag : 'ul',
30093                             cls : 'roo-navigation-bar'
30094                         }
30095                     ]
30096                 },
30097                 
30098                 {
30099                     tag : 'div',
30100                     cls : 'roo-navigation-bottom-bar'
30101                 }
30102             ]
30103             
30104         };
30105         
30106         return cfg;
30107         
30108     },
30109     
30110     initEvents: function() 
30111     {
30112         
30113     },
30114     
30115     onRender : function(ct, position) 
30116     {
30117         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30118         
30119         if(this.bullets.length){
30120             Roo.each(this.bullets, function(b){
30121                this.addItem(b);
30122             }, this);
30123         }
30124         
30125         this.format();
30126         
30127     },
30128     
30129     addItem : function(cfg)
30130     {
30131         var item = new Roo.bootstrap.NavProgressItem(cfg);
30132         
30133         item.parentId = this.id;
30134         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
30135         
30136         if(cfg.html){
30137             var top = new Roo.bootstrap.Element({
30138                 tag : 'div',
30139                 cls : 'roo-navigation-bar-text'
30140             });
30141             
30142             var bottom = new Roo.bootstrap.Element({
30143                 tag : 'div',
30144                 cls : 'roo-navigation-bar-text'
30145             });
30146             
30147             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
30148             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
30149             
30150             var topText = new Roo.bootstrap.Element({
30151                 tag : 'span',
30152                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
30153             });
30154             
30155             var bottomText = new Roo.bootstrap.Element({
30156                 tag : 'span',
30157                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
30158             });
30159             
30160             topText.onRender(top.el, null);
30161             bottomText.onRender(bottom.el, null);
30162             
30163             item.topEl = top;
30164             item.bottomEl = bottom;
30165         }
30166         
30167         this.barItems.push(item);
30168         
30169         return item;
30170     },
30171     
30172     getActive : function()
30173     {
30174         var active = false;
30175         
30176         Roo.each(this.barItems, function(v){
30177             
30178             if (!v.isActive()) {
30179                 return;
30180             }
30181             
30182             active = v;
30183             return false;
30184             
30185         });
30186         
30187         return active;
30188     },
30189     
30190     setActiveItem : function(item)
30191     {
30192         var prev = false;
30193         
30194         Roo.each(this.barItems, function(v){
30195             if (v.rid == item.rid) {
30196                 return ;
30197             }
30198             
30199             if (v.isActive()) {
30200                 v.setActive(false);
30201                 prev = v;
30202             }
30203         });
30204
30205         item.setActive(true);
30206         
30207         this.fireEvent('changed', this, item, prev);
30208     },
30209     
30210     getBarItem: function(rid)
30211     {
30212         var ret = false;
30213         
30214         Roo.each(this.barItems, function(e) {
30215             if (e.rid != rid) {
30216                 return;
30217             }
30218             
30219             ret =  e;
30220             return false;
30221         });
30222         
30223         return ret;
30224     },
30225     
30226     indexOfItem : function(item)
30227     {
30228         var index = false;
30229         
30230         Roo.each(this.barItems, function(v, i){
30231             
30232             if (v.rid != item.rid) {
30233                 return;
30234             }
30235             
30236             index = i;
30237             return false
30238         });
30239         
30240         return index;
30241     },
30242     
30243     setActiveNext : function()
30244     {
30245         var i = this.indexOfItem(this.getActive());
30246         
30247         if (i > this.barItems.length) {
30248             return;
30249         }
30250         
30251         this.setActiveItem(this.barItems[i+1]);
30252     },
30253     
30254     setActivePrev : function()
30255     {
30256         var i = this.indexOfItem(this.getActive());
30257         
30258         if (i  < 1) {
30259             return;
30260         }
30261         
30262         this.setActiveItem(this.barItems[i-1]);
30263     },
30264     
30265     format : function()
30266     {
30267         if(!this.barItems.length){
30268             return;
30269         }
30270      
30271         var width = 100 / this.barItems.length;
30272         
30273         Roo.each(this.barItems, function(i){
30274             i.el.setStyle('width', width + '%');
30275             i.topEl.el.setStyle('width', width + '%');
30276             i.bottomEl.el.setStyle('width', width + '%');
30277         }, this);
30278         
30279     }
30280     
30281 });
30282 /*
30283  * - LGPL
30284  *
30285  * Nav Progress Item
30286  * 
30287  */
30288
30289 /**
30290  * @class Roo.bootstrap.NavProgressItem
30291  * @extends Roo.bootstrap.Component
30292  * Bootstrap NavProgressItem class
30293  * @cfg {String} rid the reference id
30294  * @cfg {Boolean} active (true|false) Is item active default false
30295  * @cfg {Boolean} disabled (true|false) Is item active default false
30296  * @cfg {String} html
30297  * @cfg {String} position (top|bottom) text position default bottom
30298  * @cfg {String} icon show icon instead of number
30299  * 
30300  * @constructor
30301  * Create a new NavProgressItem
30302  * @param {Object} config The config object
30303  */
30304 Roo.bootstrap.NavProgressItem = function(config){
30305     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
30306     this.addEvents({
30307         // raw events
30308         /**
30309          * @event click
30310          * The raw click event for the entire grid.
30311          * @param {Roo.bootstrap.NavProgressItem} this
30312          * @param {Roo.EventObject} e
30313          */
30314         "click" : true
30315     });
30316    
30317 };
30318
30319 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
30320     
30321     rid : '',
30322     active : false,
30323     disabled : false,
30324     html : '',
30325     position : 'bottom',
30326     icon : false,
30327     
30328     getAutoCreate : function()
30329     {
30330         var iconCls = 'roo-navigation-bar-item-icon';
30331         
30332         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
30333         
30334         var cfg = {
30335             tag: 'li',
30336             cls: 'roo-navigation-bar-item',
30337             cn : [
30338                 {
30339                     tag : 'i',
30340                     cls : iconCls
30341                 }
30342             ]
30343         };
30344         
30345         if(this.active){
30346             cfg.cls += ' active';
30347         }
30348         if(this.disabled){
30349             cfg.cls += ' disabled';
30350         }
30351         
30352         return cfg;
30353     },
30354     
30355     disable : function()
30356     {
30357         this.setDisabled(true);
30358     },
30359     
30360     enable : function()
30361     {
30362         this.setDisabled(false);
30363     },
30364     
30365     initEvents: function() 
30366     {
30367         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
30368         
30369         this.iconEl.on('click', this.onClick, this);
30370     },
30371     
30372     onClick : function(e)
30373     {
30374         e.preventDefault();
30375         
30376         if(this.disabled){
30377             return;
30378         }
30379         
30380         if(this.fireEvent('click', this, e) === false){
30381             return;
30382         };
30383         
30384         this.parent().setActiveItem(this);
30385     },
30386     
30387     isActive: function () 
30388     {
30389         return this.active;
30390     },
30391     
30392     setActive : function(state)
30393     {
30394         if(this.active == state){
30395             return;
30396         }
30397         
30398         this.active = state;
30399         
30400         if (state) {
30401             this.el.addClass('active');
30402             return;
30403         }
30404         
30405         this.el.removeClass('active');
30406         
30407         return;
30408     },
30409     
30410     setDisabled : function(state)
30411     {
30412         if(this.disabled == state){
30413             return;
30414         }
30415         
30416         this.disabled = state;
30417         
30418         if (state) {
30419             this.el.addClass('disabled');
30420             return;
30421         }
30422         
30423         this.el.removeClass('disabled');
30424     },
30425     
30426     tooltipEl : function()
30427     {
30428         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
30429     }
30430 });
30431  
30432
30433  /*
30434  * - LGPL
30435  *
30436  * FieldLabel
30437  * 
30438  */
30439
30440 /**
30441  * @class Roo.bootstrap.FieldLabel
30442  * @extends Roo.bootstrap.Component
30443  * Bootstrap FieldLabel class
30444  * @cfg {String} html contents of the element
30445  * @cfg {String} tag tag of the element default label
30446  * @cfg {String} cls class of the element
30447  * @cfg {String} target label target 
30448  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
30449  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
30450  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
30451  * @cfg {String} iconTooltip default "This field is required"
30452  * @cfg {String} indicatorpos (left|right) default left
30453  * 
30454  * @constructor
30455  * Create a new FieldLabel
30456  * @param {Object} config The config object
30457  */
30458
30459 Roo.bootstrap.FieldLabel = function(config){
30460     Roo.bootstrap.Element.superclass.constructor.call(this, config);
30461     
30462     this.addEvents({
30463             /**
30464              * @event invalid
30465              * Fires after the field has been marked as invalid.
30466              * @param {Roo.form.FieldLabel} this
30467              * @param {String} msg The validation message
30468              */
30469             invalid : true,
30470             /**
30471              * @event valid
30472              * Fires after the field has been validated with no errors.
30473              * @param {Roo.form.FieldLabel} this
30474              */
30475             valid : true
30476         });
30477 };
30478
30479 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
30480     
30481     tag: 'label',
30482     cls: '',
30483     html: '',
30484     target: '',
30485     allowBlank : true,
30486     invalidClass : 'has-warning',
30487     validClass : 'has-success',
30488     iconTooltip : 'This field is required',
30489     indicatorpos : 'left',
30490     
30491     getAutoCreate : function(){
30492         
30493         var cls = "";
30494         if (!this.allowBlank) {
30495             cls  = "visible";
30496         }
30497         
30498         var cfg = {
30499             tag : this.tag,
30500             cls : 'roo-bootstrap-field-label ' + this.cls,
30501             for : this.target,
30502             cn : [
30503                 {
30504                     tag : 'i',
30505                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
30506                     tooltip : this.iconTooltip
30507                 },
30508                 {
30509                     tag : 'span',
30510                     html : this.html
30511                 }
30512             ] 
30513         };
30514         
30515         if(this.indicatorpos == 'right'){
30516             var cfg = {
30517                 tag : this.tag,
30518                 cls : 'roo-bootstrap-field-label ' + this.cls,
30519                 for : this.target,
30520                 cn : [
30521                     {
30522                         tag : 'span',
30523                         html : this.html
30524                     },
30525                     {
30526                         tag : 'i',
30527                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
30528                         tooltip : this.iconTooltip
30529                     }
30530                 ] 
30531             };
30532         }
30533         
30534         return cfg;
30535     },
30536     
30537     initEvents: function() 
30538     {
30539         Roo.bootstrap.Element.superclass.initEvents.call(this);
30540         
30541         this.indicator = this.indicatorEl();
30542         
30543         if(this.indicator){
30544             this.indicator.removeClass('visible');
30545             this.indicator.addClass('invisible');
30546         }
30547         
30548         Roo.bootstrap.FieldLabel.register(this);
30549     },
30550     
30551     indicatorEl : function()
30552     {
30553         var indicator = this.el.select('i.roo-required-indicator',true).first();
30554         
30555         if(!indicator){
30556             return false;
30557         }
30558         
30559         return indicator;
30560         
30561     },
30562     
30563     /**
30564      * Mark this field as valid
30565      */
30566     markValid : function()
30567     {
30568         if(this.indicator){
30569             this.indicator.removeClass('visible');
30570             this.indicator.addClass('invisible');
30571         }
30572         if (Roo.bootstrap.version == 3) {
30573             this.el.removeClass(this.invalidClass);
30574             this.el.addClass(this.validClass);
30575         } else {
30576             this.el.removeClass('is-invalid');
30577             this.el.addClass('is-valid');
30578         }
30579         
30580         
30581         this.fireEvent('valid', this);
30582     },
30583     
30584     /**
30585      * Mark this field as invalid
30586      * @param {String} msg The validation message
30587      */
30588     markInvalid : function(msg)
30589     {
30590         if(this.indicator){
30591             this.indicator.removeClass('invisible');
30592             this.indicator.addClass('visible');
30593         }
30594           if (Roo.bootstrap.version == 3) {
30595             this.el.removeClass(this.validClass);
30596             this.el.addClass(this.invalidClass);
30597         } else {
30598             this.el.removeClass('is-valid');
30599             this.el.addClass('is-invalid');
30600         }
30601         
30602         
30603         this.fireEvent('invalid', this, msg);
30604     }
30605     
30606    
30607 });
30608
30609 Roo.apply(Roo.bootstrap.FieldLabel, {
30610     
30611     groups: {},
30612     
30613      /**
30614     * register a FieldLabel Group
30615     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
30616     */
30617     register : function(label)
30618     {
30619         if(this.groups.hasOwnProperty(label.target)){
30620             return;
30621         }
30622      
30623         this.groups[label.target] = label;
30624         
30625     },
30626     /**
30627     * fetch a FieldLabel Group based on the target
30628     * @param {string} target
30629     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
30630     */
30631     get: function(target) {
30632         if (typeof(this.groups[target]) == 'undefined') {
30633             return false;
30634         }
30635         
30636         return this.groups[target] ;
30637     }
30638 });
30639
30640  
30641
30642  /*
30643  * - LGPL
30644  *
30645  * page DateSplitField.
30646  * 
30647  */
30648
30649
30650 /**
30651  * @class Roo.bootstrap.DateSplitField
30652  * @extends Roo.bootstrap.Component
30653  * Bootstrap DateSplitField class
30654  * @cfg {string} fieldLabel - the label associated
30655  * @cfg {Number} labelWidth set the width of label (0-12)
30656  * @cfg {String} labelAlign (top|left)
30657  * @cfg {Boolean} dayAllowBlank (true|false) default false
30658  * @cfg {Boolean} monthAllowBlank (true|false) default false
30659  * @cfg {Boolean} yearAllowBlank (true|false) default false
30660  * @cfg {string} dayPlaceholder 
30661  * @cfg {string} monthPlaceholder
30662  * @cfg {string} yearPlaceholder
30663  * @cfg {string} dayFormat default 'd'
30664  * @cfg {string} monthFormat default 'm'
30665  * @cfg {string} yearFormat default 'Y'
30666  * @cfg {Number} labellg set the width of label (1-12)
30667  * @cfg {Number} labelmd set the width of label (1-12)
30668  * @cfg {Number} labelsm set the width of label (1-12)
30669  * @cfg {Number} labelxs set the width of label (1-12)
30670
30671  *     
30672  * @constructor
30673  * Create a new DateSplitField
30674  * @param {Object} config The config object
30675  */
30676
30677 Roo.bootstrap.DateSplitField = function(config){
30678     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30679     
30680     this.addEvents({
30681         // raw events
30682          /**
30683          * @event years
30684          * getting the data of years
30685          * @param {Roo.bootstrap.DateSplitField} this
30686          * @param {Object} years
30687          */
30688         "years" : true,
30689         /**
30690          * @event days
30691          * getting the data of days
30692          * @param {Roo.bootstrap.DateSplitField} this
30693          * @param {Object} days
30694          */
30695         "days" : true,
30696         /**
30697          * @event invalid
30698          * Fires after the field has been marked as invalid.
30699          * @param {Roo.form.Field} this
30700          * @param {String} msg The validation message
30701          */
30702         invalid : true,
30703        /**
30704          * @event valid
30705          * Fires after the field has been validated with no errors.
30706          * @param {Roo.form.Field} this
30707          */
30708         valid : true
30709     });
30710 };
30711
30712 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30713     
30714     fieldLabel : '',
30715     labelAlign : 'top',
30716     labelWidth : 3,
30717     dayAllowBlank : false,
30718     monthAllowBlank : false,
30719     yearAllowBlank : false,
30720     dayPlaceholder : '',
30721     monthPlaceholder : '',
30722     yearPlaceholder : '',
30723     dayFormat : 'd',
30724     monthFormat : 'm',
30725     yearFormat : 'Y',
30726     isFormField : true,
30727     labellg : 0,
30728     labelmd : 0,
30729     labelsm : 0,
30730     labelxs : 0,
30731     
30732     getAutoCreate : function()
30733     {
30734         var cfg = {
30735             tag : 'div',
30736             cls : 'row roo-date-split-field-group',
30737             cn : [
30738                 {
30739                     tag : 'input',
30740                     type : 'hidden',
30741                     cls : 'form-hidden-field roo-date-split-field-group-value',
30742                     name : this.name
30743                 }
30744             ]
30745         };
30746         
30747         var labelCls = 'col-md-12';
30748         var contentCls = 'col-md-4';
30749         
30750         if(this.fieldLabel){
30751             
30752             var label = {
30753                 tag : 'div',
30754                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30755                 cn : [
30756                     {
30757                         tag : 'label',
30758                         html : this.fieldLabel
30759                     }
30760                 ]
30761             };
30762             
30763             if(this.labelAlign == 'left'){
30764             
30765                 if(this.labelWidth > 12){
30766                     label.style = "width: " + this.labelWidth + 'px';
30767                 }
30768
30769                 if(this.labelWidth < 13 && this.labelmd == 0){
30770                     this.labelmd = this.labelWidth;
30771                 }
30772
30773                 if(this.labellg > 0){
30774                     labelCls = ' col-lg-' + this.labellg;
30775                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30776                 }
30777
30778                 if(this.labelmd > 0){
30779                     labelCls = ' col-md-' + this.labelmd;
30780                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30781                 }
30782
30783                 if(this.labelsm > 0){
30784                     labelCls = ' col-sm-' + this.labelsm;
30785                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30786                 }
30787
30788                 if(this.labelxs > 0){
30789                     labelCls = ' col-xs-' + this.labelxs;
30790                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30791                 }
30792             }
30793             
30794             label.cls += ' ' + labelCls;
30795             
30796             cfg.cn.push(label);
30797         }
30798         
30799         Roo.each(['day', 'month', 'year'], function(t){
30800             cfg.cn.push({
30801                 tag : 'div',
30802                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30803             });
30804         }, this);
30805         
30806         return cfg;
30807     },
30808     
30809     inputEl: function ()
30810     {
30811         return this.el.select('.roo-date-split-field-group-value', true).first();
30812     },
30813     
30814     onRender : function(ct, position) 
30815     {
30816         var _this = this;
30817         
30818         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30819         
30820         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30821         
30822         this.dayField = new Roo.bootstrap.ComboBox({
30823             allowBlank : this.dayAllowBlank,
30824             alwaysQuery : true,
30825             displayField : 'value',
30826             editable : false,
30827             fieldLabel : '',
30828             forceSelection : true,
30829             mode : 'local',
30830             placeholder : this.dayPlaceholder,
30831             selectOnFocus : true,
30832             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30833             triggerAction : 'all',
30834             typeAhead : true,
30835             valueField : 'value',
30836             store : new Roo.data.SimpleStore({
30837                 data : (function() {    
30838                     var days = [];
30839                     _this.fireEvent('days', _this, days);
30840                     return days;
30841                 })(),
30842                 fields : [ 'value' ]
30843             }),
30844             listeners : {
30845                 select : function (_self, record, index)
30846                 {
30847                     _this.setValue(_this.getValue());
30848                 }
30849             }
30850         });
30851
30852         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30853         
30854         this.monthField = new Roo.bootstrap.MonthField({
30855             after : '<i class=\"fa fa-calendar\"></i>',
30856             allowBlank : this.monthAllowBlank,
30857             placeholder : this.monthPlaceholder,
30858             readOnly : true,
30859             listeners : {
30860                 render : function (_self)
30861                 {
30862                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30863                         e.preventDefault();
30864                         _self.focus();
30865                     });
30866                 },
30867                 select : function (_self, oldvalue, newvalue)
30868                 {
30869                     _this.setValue(_this.getValue());
30870                 }
30871             }
30872         });
30873         
30874         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30875         
30876         this.yearField = new Roo.bootstrap.ComboBox({
30877             allowBlank : this.yearAllowBlank,
30878             alwaysQuery : true,
30879             displayField : 'value',
30880             editable : false,
30881             fieldLabel : '',
30882             forceSelection : true,
30883             mode : 'local',
30884             placeholder : this.yearPlaceholder,
30885             selectOnFocus : true,
30886             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30887             triggerAction : 'all',
30888             typeAhead : true,
30889             valueField : 'value',
30890             store : new Roo.data.SimpleStore({
30891                 data : (function() {
30892                     var years = [];
30893                     _this.fireEvent('years', _this, years);
30894                     return years;
30895                 })(),
30896                 fields : [ 'value' ]
30897             }),
30898             listeners : {
30899                 select : function (_self, record, index)
30900                 {
30901                     _this.setValue(_this.getValue());
30902                 }
30903             }
30904         });
30905
30906         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30907     },
30908     
30909     setValue : function(v, format)
30910     {
30911         this.inputEl.dom.value = v;
30912         
30913         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30914         
30915         var d = Date.parseDate(v, f);
30916         
30917         if(!d){
30918             this.validate();
30919             return;
30920         }
30921         
30922         this.setDay(d.format(this.dayFormat));
30923         this.setMonth(d.format(this.monthFormat));
30924         this.setYear(d.format(this.yearFormat));
30925         
30926         this.validate();
30927         
30928         return;
30929     },
30930     
30931     setDay : function(v)
30932     {
30933         this.dayField.setValue(v);
30934         this.inputEl.dom.value = this.getValue();
30935         this.validate();
30936         return;
30937     },
30938     
30939     setMonth : function(v)
30940     {
30941         this.monthField.setValue(v, true);
30942         this.inputEl.dom.value = this.getValue();
30943         this.validate();
30944         return;
30945     },
30946     
30947     setYear : function(v)
30948     {
30949         this.yearField.setValue(v);
30950         this.inputEl.dom.value = this.getValue();
30951         this.validate();
30952         return;
30953     },
30954     
30955     getDay : function()
30956     {
30957         return this.dayField.getValue();
30958     },
30959     
30960     getMonth : function()
30961     {
30962         return this.monthField.getValue();
30963     },
30964     
30965     getYear : function()
30966     {
30967         return this.yearField.getValue();
30968     },
30969     
30970     getValue : function()
30971     {
30972         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30973         
30974         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30975         
30976         return date;
30977     },
30978     
30979     reset : function()
30980     {
30981         this.setDay('');
30982         this.setMonth('');
30983         this.setYear('');
30984         this.inputEl.dom.value = '';
30985         this.validate();
30986         return;
30987     },
30988     
30989     validate : function()
30990     {
30991         var d = this.dayField.validate();
30992         var m = this.monthField.validate();
30993         var y = this.yearField.validate();
30994         
30995         var valid = true;
30996         
30997         if(
30998                 (!this.dayAllowBlank && !d) ||
30999                 (!this.monthAllowBlank && !m) ||
31000                 (!this.yearAllowBlank && !y)
31001         ){
31002             valid = false;
31003         }
31004         
31005         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
31006             return valid;
31007         }
31008         
31009         if(valid){
31010             this.markValid();
31011             return valid;
31012         }
31013         
31014         this.markInvalid();
31015         
31016         return valid;
31017     },
31018     
31019     markValid : function()
31020     {
31021         
31022         var label = this.el.select('label', true).first();
31023         var icon = this.el.select('i.fa-star', true).first();
31024
31025         if(label && icon){
31026             icon.remove();
31027         }
31028         
31029         this.fireEvent('valid', this);
31030     },
31031     
31032      /**
31033      * Mark this field as invalid
31034      * @param {String} msg The validation message
31035      */
31036     markInvalid : function(msg)
31037     {
31038         
31039         var label = this.el.select('label', true).first();
31040         var icon = this.el.select('i.fa-star', true).first();
31041
31042         if(label && !icon){
31043             this.el.select('.roo-date-split-field-label', true).createChild({
31044                 tag : 'i',
31045                 cls : 'text-danger fa fa-lg fa-star',
31046                 tooltip : 'This field is required',
31047                 style : 'margin-right:5px;'
31048             }, label, true);
31049         }
31050         
31051         this.fireEvent('invalid', this, msg);
31052     },
31053     
31054     clearInvalid : function()
31055     {
31056         var label = this.el.select('label', true).first();
31057         var icon = this.el.select('i.fa-star', true).first();
31058
31059         if(label && icon){
31060             icon.remove();
31061         }
31062         
31063         this.fireEvent('valid', this);
31064     },
31065     
31066     getName: function()
31067     {
31068         return this.name;
31069     }
31070     
31071 });
31072
31073  /**
31074  *
31075  * This is based on 
31076  * http://masonry.desandro.com
31077  *
31078  * The idea is to render all the bricks based on vertical width...
31079  *
31080  * The original code extends 'outlayer' - we might need to use that....
31081  * 
31082  */
31083
31084
31085 /**
31086  * @class Roo.bootstrap.LayoutMasonry
31087  * @extends Roo.bootstrap.Component
31088  * Bootstrap Layout Masonry class
31089  * 
31090  * @constructor
31091  * Create a new Element
31092  * @param {Object} config The config object
31093  */
31094
31095 Roo.bootstrap.LayoutMasonry = function(config){
31096     
31097     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
31098     
31099     this.bricks = [];
31100     
31101     Roo.bootstrap.LayoutMasonry.register(this);
31102     
31103     this.addEvents({
31104         // raw events
31105         /**
31106          * @event layout
31107          * Fire after layout the items
31108          * @param {Roo.bootstrap.LayoutMasonry} this
31109          * @param {Roo.EventObject} e
31110          */
31111         "layout" : true
31112     });
31113     
31114 };
31115
31116 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
31117     
31118     /**
31119      * @cfg {Boolean} isLayoutInstant = no animation?
31120      */   
31121     isLayoutInstant : false, // needed?
31122    
31123     /**
31124      * @cfg {Number} boxWidth  width of the columns
31125      */   
31126     boxWidth : 450,
31127     
31128       /**
31129      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
31130      */   
31131     boxHeight : 0,
31132     
31133     /**
31134      * @cfg {Number} padWidth padding below box..
31135      */   
31136     padWidth : 10, 
31137     
31138     /**
31139      * @cfg {Number} gutter gutter width..
31140      */   
31141     gutter : 10,
31142     
31143      /**
31144      * @cfg {Number} maxCols maximum number of columns
31145      */   
31146     
31147     maxCols: 0,
31148     
31149     /**
31150      * @cfg {Boolean} isAutoInitial defalut true
31151      */   
31152     isAutoInitial : true, 
31153     
31154     containerWidth: 0,
31155     
31156     /**
31157      * @cfg {Boolean} isHorizontal defalut false
31158      */   
31159     isHorizontal : false, 
31160
31161     currentSize : null,
31162     
31163     tag: 'div',
31164     
31165     cls: '',
31166     
31167     bricks: null, //CompositeElement
31168     
31169     cols : 1,
31170     
31171     _isLayoutInited : false,
31172     
31173 //    isAlternative : false, // only use for vertical layout...
31174     
31175     /**
31176      * @cfg {Number} alternativePadWidth padding below box..
31177      */   
31178     alternativePadWidth : 50,
31179     
31180     selectedBrick : [],
31181     
31182     getAutoCreate : function(){
31183         
31184         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
31185         
31186         var cfg = {
31187             tag: this.tag,
31188             cls: 'blog-masonary-wrapper ' + this.cls,
31189             cn : {
31190                 cls : 'mas-boxes masonary'
31191             }
31192         };
31193         
31194         return cfg;
31195     },
31196     
31197     getChildContainer: function( )
31198     {
31199         if (this.boxesEl) {
31200             return this.boxesEl;
31201         }
31202         
31203         this.boxesEl = this.el.select('.mas-boxes').first();
31204         
31205         return this.boxesEl;
31206     },
31207     
31208     
31209     initEvents : function()
31210     {
31211         var _this = this;
31212         
31213         if(this.isAutoInitial){
31214             Roo.log('hook children rendered');
31215             this.on('childrenrendered', function() {
31216                 Roo.log('children rendered');
31217                 _this.initial();
31218             } ,this);
31219         }
31220     },
31221     
31222     initial : function()
31223     {
31224         this.selectedBrick = [];
31225         
31226         this.currentSize = this.el.getBox(true);
31227         
31228         Roo.EventManager.onWindowResize(this.resize, this); 
31229
31230         if(!this.isAutoInitial){
31231             this.layout();
31232             return;
31233         }
31234         
31235         this.layout();
31236         
31237         return;
31238         //this.layout.defer(500,this);
31239         
31240     },
31241     
31242     resize : function()
31243     {
31244         var cs = this.el.getBox(true);
31245         
31246         if (
31247                 this.currentSize.width == cs.width && 
31248                 this.currentSize.x == cs.x && 
31249                 this.currentSize.height == cs.height && 
31250                 this.currentSize.y == cs.y 
31251         ) {
31252             Roo.log("no change in with or X or Y");
31253             return;
31254         }
31255         
31256         this.currentSize = cs;
31257         
31258         this.layout();
31259         
31260     },
31261     
31262     layout : function()
31263     {   
31264         this._resetLayout();
31265         
31266         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31267         
31268         this.layoutItems( isInstant );
31269       
31270         this._isLayoutInited = true;
31271         
31272         this.fireEvent('layout', this);
31273         
31274     },
31275     
31276     _resetLayout : function()
31277     {
31278         if(this.isHorizontal){
31279             this.horizontalMeasureColumns();
31280             return;
31281         }
31282         
31283         this.verticalMeasureColumns();
31284         
31285     },
31286     
31287     verticalMeasureColumns : function()
31288     {
31289         this.getContainerWidth();
31290         
31291 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31292 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
31293 //            return;
31294 //        }
31295         
31296         var boxWidth = this.boxWidth + this.padWidth;
31297         
31298         if(this.containerWidth < this.boxWidth){
31299             boxWidth = this.containerWidth
31300         }
31301         
31302         var containerWidth = this.containerWidth;
31303         
31304         var cols = Math.floor(containerWidth / boxWidth);
31305         
31306         this.cols = Math.max( cols, 1 );
31307         
31308         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31309         
31310         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
31311         
31312         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
31313         
31314         this.colWidth = boxWidth + avail - this.padWidth;
31315         
31316         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
31317         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
31318     },
31319     
31320     horizontalMeasureColumns : function()
31321     {
31322         this.getContainerWidth();
31323         
31324         var boxWidth = this.boxWidth;
31325         
31326         if(this.containerWidth < boxWidth){
31327             boxWidth = this.containerWidth;
31328         }
31329         
31330         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
31331         
31332         this.el.setHeight(boxWidth);
31333         
31334     },
31335     
31336     getContainerWidth : function()
31337     {
31338         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31339     },
31340     
31341     layoutItems : function( isInstant )
31342     {
31343         Roo.log(this.bricks);
31344         
31345         var items = Roo.apply([], this.bricks);
31346         
31347         if(this.isHorizontal){
31348             this._horizontalLayoutItems( items , isInstant );
31349             return;
31350         }
31351         
31352 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
31353 //            this._verticalAlternativeLayoutItems( items , isInstant );
31354 //            return;
31355 //        }
31356         
31357         this._verticalLayoutItems( items , isInstant );
31358         
31359     },
31360     
31361     _verticalLayoutItems : function ( items , isInstant)
31362     {
31363         if ( !items || !items.length ) {
31364             return;
31365         }
31366         
31367         var standard = [
31368             ['xs', 'xs', 'xs', 'tall'],
31369             ['xs', 'xs', 'tall'],
31370             ['xs', 'xs', 'sm'],
31371             ['xs', 'xs', 'xs'],
31372             ['xs', 'tall'],
31373             ['xs', 'sm'],
31374             ['xs', 'xs'],
31375             ['xs'],
31376             
31377             ['sm', 'xs', 'xs'],
31378             ['sm', 'xs'],
31379             ['sm'],
31380             
31381             ['tall', 'xs', 'xs', 'xs'],
31382             ['tall', 'xs', 'xs'],
31383             ['tall', 'xs'],
31384             ['tall']
31385             
31386         ];
31387         
31388         var queue = [];
31389         
31390         var boxes = [];
31391         
31392         var box = [];
31393         
31394         Roo.each(items, function(item, k){
31395             
31396             switch (item.size) {
31397                 // these layouts take up a full box,
31398                 case 'md' :
31399                 case 'md-left' :
31400                 case 'md-right' :
31401                 case 'wide' :
31402                     
31403                     if(box.length){
31404                         boxes.push(box);
31405                         box = [];
31406                     }
31407                     
31408                     boxes.push([item]);
31409                     
31410                     break;
31411                     
31412                 case 'xs' :
31413                 case 'sm' :
31414                 case 'tall' :
31415                     
31416                     box.push(item);
31417                     
31418                     break;
31419                 default :
31420                     break;
31421                     
31422             }
31423             
31424         }, this);
31425         
31426         if(box.length){
31427             boxes.push(box);
31428             box = [];
31429         }
31430         
31431         var filterPattern = function(box, length)
31432         {
31433             if(!box.length){
31434                 return;
31435             }
31436             
31437             var match = false;
31438             
31439             var pattern = box.slice(0, length);
31440             
31441             var format = [];
31442             
31443             Roo.each(pattern, function(i){
31444                 format.push(i.size);
31445             }, this);
31446             
31447             Roo.each(standard, function(s){
31448                 
31449                 if(String(s) != String(format)){
31450                     return;
31451                 }
31452                 
31453                 match = true;
31454                 return false;
31455                 
31456             }, this);
31457             
31458             if(!match && length == 1){
31459                 return;
31460             }
31461             
31462             if(!match){
31463                 filterPattern(box, length - 1);
31464                 return;
31465             }
31466                 
31467             queue.push(pattern);
31468
31469             box = box.slice(length, box.length);
31470
31471             filterPattern(box, 4);
31472
31473             return;
31474             
31475         }
31476         
31477         Roo.each(boxes, function(box, k){
31478             
31479             if(!box.length){
31480                 return;
31481             }
31482             
31483             if(box.length == 1){
31484                 queue.push(box);
31485                 return;
31486             }
31487             
31488             filterPattern(box, 4);
31489             
31490         }, this);
31491         
31492         this._processVerticalLayoutQueue( queue, isInstant );
31493         
31494     },
31495     
31496 //    _verticalAlternativeLayoutItems : function( items , isInstant )
31497 //    {
31498 //        if ( !items || !items.length ) {
31499 //            return;
31500 //        }
31501 //
31502 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
31503 //        
31504 //    },
31505     
31506     _horizontalLayoutItems : function ( items , isInstant)
31507     {
31508         if ( !items || !items.length || items.length < 3) {
31509             return;
31510         }
31511         
31512         items.reverse();
31513         
31514         var eItems = items.slice(0, 3);
31515         
31516         items = items.slice(3, items.length);
31517         
31518         var standard = [
31519             ['xs', 'xs', 'xs', 'wide'],
31520             ['xs', 'xs', 'wide'],
31521             ['xs', 'xs', 'sm'],
31522             ['xs', 'xs', 'xs'],
31523             ['xs', 'wide'],
31524             ['xs', 'sm'],
31525             ['xs', 'xs'],
31526             ['xs'],
31527             
31528             ['sm', 'xs', 'xs'],
31529             ['sm', 'xs'],
31530             ['sm'],
31531             
31532             ['wide', 'xs', 'xs', 'xs'],
31533             ['wide', 'xs', 'xs'],
31534             ['wide', 'xs'],
31535             ['wide'],
31536             
31537             ['wide-thin']
31538         ];
31539         
31540         var queue = [];
31541         
31542         var boxes = [];
31543         
31544         var box = [];
31545         
31546         Roo.each(items, function(item, k){
31547             
31548             switch (item.size) {
31549                 case 'md' :
31550                 case 'md-left' :
31551                 case 'md-right' :
31552                 case 'tall' :
31553                     
31554                     if(box.length){
31555                         boxes.push(box);
31556                         box = [];
31557                     }
31558                     
31559                     boxes.push([item]);
31560                     
31561                     break;
31562                     
31563                 case 'xs' :
31564                 case 'sm' :
31565                 case 'wide' :
31566                 case 'wide-thin' :
31567                     
31568                     box.push(item);
31569                     
31570                     break;
31571                 default :
31572                     break;
31573                     
31574             }
31575             
31576         }, this);
31577         
31578         if(box.length){
31579             boxes.push(box);
31580             box = [];
31581         }
31582         
31583         var filterPattern = function(box, length)
31584         {
31585             if(!box.length){
31586                 return;
31587             }
31588             
31589             var match = false;
31590             
31591             var pattern = box.slice(0, length);
31592             
31593             var format = [];
31594             
31595             Roo.each(pattern, function(i){
31596                 format.push(i.size);
31597             }, this);
31598             
31599             Roo.each(standard, function(s){
31600                 
31601                 if(String(s) != String(format)){
31602                     return;
31603                 }
31604                 
31605                 match = true;
31606                 return false;
31607                 
31608             }, this);
31609             
31610             if(!match && length == 1){
31611                 return;
31612             }
31613             
31614             if(!match){
31615                 filterPattern(box, length - 1);
31616                 return;
31617             }
31618                 
31619             queue.push(pattern);
31620
31621             box = box.slice(length, box.length);
31622
31623             filterPattern(box, 4);
31624
31625             return;
31626             
31627         }
31628         
31629         Roo.each(boxes, function(box, k){
31630             
31631             if(!box.length){
31632                 return;
31633             }
31634             
31635             if(box.length == 1){
31636                 queue.push(box);
31637                 return;
31638             }
31639             
31640             filterPattern(box, 4);
31641             
31642         }, this);
31643         
31644         
31645         var prune = [];
31646         
31647         var pos = this.el.getBox(true);
31648         
31649         var minX = pos.x;
31650         
31651         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31652         
31653         var hit_end = false;
31654         
31655         Roo.each(queue, function(box){
31656             
31657             if(hit_end){
31658                 
31659                 Roo.each(box, function(b){
31660                 
31661                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31662                     b.el.hide();
31663
31664                 }, this);
31665
31666                 return;
31667             }
31668             
31669             var mx = 0;
31670             
31671             Roo.each(box, function(b){
31672                 
31673                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
31674                 b.el.show();
31675
31676                 mx = Math.max(mx, b.x);
31677                 
31678             }, this);
31679             
31680             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31681             
31682             if(maxX < minX){
31683                 
31684                 Roo.each(box, function(b){
31685                 
31686                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31687                     b.el.hide();
31688                     
31689                 }, this);
31690                 
31691                 hit_end = true;
31692                 
31693                 return;
31694             }
31695             
31696             prune.push(box);
31697             
31698         }, this);
31699         
31700         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31701     },
31702     
31703     /** Sets position of item in DOM
31704     * @param {Element} item
31705     * @param {Number} x - horizontal position
31706     * @param {Number} y - vertical position
31707     * @param {Boolean} isInstant - disables transitions
31708     */
31709     _processVerticalLayoutQueue : function( queue, isInstant )
31710     {
31711         var pos = this.el.getBox(true);
31712         var x = pos.x;
31713         var y = pos.y;
31714         var maxY = [];
31715         
31716         for (var i = 0; i < this.cols; i++){
31717             maxY[i] = pos.y;
31718         }
31719         
31720         Roo.each(queue, function(box, k){
31721             
31722             var col = k % this.cols;
31723             
31724             Roo.each(box, function(b,kk){
31725                 
31726                 b.el.position('absolute');
31727                 
31728                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31729                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31730                 
31731                 if(b.size == 'md-left' || b.size == 'md-right'){
31732                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31733                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31734                 }
31735                 
31736                 b.el.setWidth(width);
31737                 b.el.setHeight(height);
31738                 // iframe?
31739                 b.el.select('iframe',true).setSize(width,height);
31740                 
31741             }, this);
31742             
31743             for (var i = 0; i < this.cols; i++){
31744                 
31745                 if(maxY[i] < maxY[col]){
31746                     col = i;
31747                     continue;
31748                 }
31749                 
31750                 col = Math.min(col, i);
31751                 
31752             }
31753             
31754             x = pos.x + col * (this.colWidth + this.padWidth);
31755             
31756             y = maxY[col];
31757             
31758             var positions = [];
31759             
31760             switch (box.length){
31761                 case 1 :
31762                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31763                     break;
31764                 case 2 :
31765                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31766                     break;
31767                 case 3 :
31768                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31769                     break;
31770                 case 4 :
31771                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31772                     break;
31773                 default :
31774                     break;
31775             }
31776             
31777             Roo.each(box, function(b,kk){
31778                 
31779                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31780                 
31781                 var sz = b.el.getSize();
31782                 
31783                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31784                 
31785             }, this);
31786             
31787         }, this);
31788         
31789         var mY = 0;
31790         
31791         for (var i = 0; i < this.cols; i++){
31792             mY = Math.max(mY, maxY[i]);
31793         }
31794         
31795         this.el.setHeight(mY - pos.y);
31796         
31797     },
31798     
31799 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31800 //    {
31801 //        var pos = this.el.getBox(true);
31802 //        var x = pos.x;
31803 //        var y = pos.y;
31804 //        var maxX = pos.right;
31805 //        
31806 //        var maxHeight = 0;
31807 //        
31808 //        Roo.each(items, function(item, k){
31809 //            
31810 //            var c = k % 2;
31811 //            
31812 //            item.el.position('absolute');
31813 //                
31814 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31815 //
31816 //            item.el.setWidth(width);
31817 //
31818 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31819 //
31820 //            item.el.setHeight(height);
31821 //            
31822 //            if(c == 0){
31823 //                item.el.setXY([x, y], isInstant ? false : true);
31824 //            } else {
31825 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31826 //            }
31827 //            
31828 //            y = y + height + this.alternativePadWidth;
31829 //            
31830 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31831 //            
31832 //        }, this);
31833 //        
31834 //        this.el.setHeight(maxHeight);
31835 //        
31836 //    },
31837     
31838     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31839     {
31840         var pos = this.el.getBox(true);
31841         
31842         var minX = pos.x;
31843         var minY = pos.y;
31844         
31845         var maxX = pos.right;
31846         
31847         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31848         
31849         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31850         
31851         Roo.each(queue, function(box, k){
31852             
31853             Roo.each(box, function(b, kk){
31854                 
31855                 b.el.position('absolute');
31856                 
31857                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31858                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31859                 
31860                 if(b.size == 'md-left' || b.size == 'md-right'){
31861                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31862                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31863                 }
31864                 
31865                 b.el.setWidth(width);
31866                 b.el.setHeight(height);
31867                 
31868             }, this);
31869             
31870             if(!box.length){
31871                 return;
31872             }
31873             
31874             var positions = [];
31875             
31876             switch (box.length){
31877                 case 1 :
31878                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31879                     break;
31880                 case 2 :
31881                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31882                     break;
31883                 case 3 :
31884                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31885                     break;
31886                 case 4 :
31887                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31888                     break;
31889                 default :
31890                     break;
31891             }
31892             
31893             Roo.each(box, function(b,kk){
31894                 
31895                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31896                 
31897                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31898                 
31899             }, this);
31900             
31901         }, this);
31902         
31903     },
31904     
31905     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31906     {
31907         Roo.each(eItems, function(b,k){
31908             
31909             b.size = (k == 0) ? 'sm' : 'xs';
31910             b.x = (k == 0) ? 2 : 1;
31911             b.y = (k == 0) ? 2 : 1;
31912             
31913             b.el.position('absolute');
31914             
31915             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31916                 
31917             b.el.setWidth(width);
31918             
31919             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31920             
31921             b.el.setHeight(height);
31922             
31923         }, this);
31924
31925         var positions = [];
31926         
31927         positions.push({
31928             x : maxX - this.unitWidth * 2 - this.gutter,
31929             y : minY
31930         });
31931         
31932         positions.push({
31933             x : maxX - this.unitWidth,
31934             y : minY + (this.unitWidth + this.gutter) * 2
31935         });
31936         
31937         positions.push({
31938             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31939             y : minY
31940         });
31941         
31942         Roo.each(eItems, function(b,k){
31943             
31944             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31945
31946         }, this);
31947         
31948     },
31949     
31950     getVerticalOneBoxColPositions : function(x, y, box)
31951     {
31952         var pos = [];
31953         
31954         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31955         
31956         if(box[0].size == 'md-left'){
31957             rand = 0;
31958         }
31959         
31960         if(box[0].size == 'md-right'){
31961             rand = 1;
31962         }
31963         
31964         pos.push({
31965             x : x + (this.unitWidth + this.gutter) * rand,
31966             y : y
31967         });
31968         
31969         return pos;
31970     },
31971     
31972     getVerticalTwoBoxColPositions : function(x, y, box)
31973     {
31974         var pos = [];
31975         
31976         if(box[0].size == 'xs'){
31977             
31978             pos.push({
31979                 x : x,
31980                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31981             });
31982
31983             pos.push({
31984                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31985                 y : y
31986             });
31987             
31988             return pos;
31989             
31990         }
31991         
31992         pos.push({
31993             x : x,
31994             y : y
31995         });
31996
31997         pos.push({
31998             x : x + (this.unitWidth + this.gutter) * 2,
31999             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
32000         });
32001         
32002         return pos;
32003         
32004     },
32005     
32006     getVerticalThreeBoxColPositions : function(x, y, box)
32007     {
32008         var pos = [];
32009         
32010         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32011             
32012             pos.push({
32013                 x : x,
32014                 y : y
32015             });
32016
32017             pos.push({
32018                 x : x + (this.unitWidth + this.gutter) * 1,
32019                 y : y
32020             });
32021             
32022             pos.push({
32023                 x : x + (this.unitWidth + this.gutter) * 2,
32024                 y : y
32025             });
32026             
32027             return pos;
32028             
32029         }
32030         
32031         if(box[0].size == 'xs' && box[1].size == 'xs'){
32032             
32033             pos.push({
32034                 x : x,
32035                 y : y
32036             });
32037
32038             pos.push({
32039                 x : x,
32040                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
32041             });
32042             
32043             pos.push({
32044                 x : x + (this.unitWidth + this.gutter) * 1,
32045                 y : y
32046             });
32047             
32048             return pos;
32049             
32050         }
32051         
32052         pos.push({
32053             x : x,
32054             y : y
32055         });
32056
32057         pos.push({
32058             x : x + (this.unitWidth + this.gutter) * 2,
32059             y : y
32060         });
32061
32062         pos.push({
32063             x : x + (this.unitWidth + this.gutter) * 2,
32064             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
32065         });
32066             
32067         return pos;
32068         
32069     },
32070     
32071     getVerticalFourBoxColPositions : function(x, y, box)
32072     {
32073         var pos = [];
32074         
32075         if(box[0].size == 'xs'){
32076             
32077             pos.push({
32078                 x : x,
32079                 y : y
32080             });
32081
32082             pos.push({
32083                 x : x,
32084                 y : y + (this.unitHeight + this.gutter) * 1
32085             });
32086             
32087             pos.push({
32088                 x : x,
32089                 y : y + (this.unitHeight + this.gutter) * 2
32090             });
32091             
32092             pos.push({
32093                 x : x + (this.unitWidth + this.gutter) * 1,
32094                 y : y
32095             });
32096             
32097             return pos;
32098             
32099         }
32100         
32101         pos.push({
32102             x : x,
32103             y : y
32104         });
32105
32106         pos.push({
32107             x : x + (this.unitWidth + this.gutter) * 2,
32108             y : y
32109         });
32110
32111         pos.push({
32112             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
32113             y : y + (this.unitHeight + this.gutter) * 1
32114         });
32115
32116         pos.push({
32117             x : x + (this.unitWidth + this.gutter) * 2,
32118             y : y + (this.unitWidth + this.gutter) * 2
32119         });
32120
32121         return pos;
32122         
32123     },
32124     
32125     getHorizontalOneBoxColPositions : function(maxX, minY, box)
32126     {
32127         var pos = [];
32128         
32129         if(box[0].size == 'md-left'){
32130             pos.push({
32131                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32132                 y : minY
32133             });
32134             
32135             return pos;
32136         }
32137         
32138         if(box[0].size == 'md-right'){
32139             pos.push({
32140                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
32141                 y : minY + (this.unitWidth + this.gutter) * 1
32142             });
32143             
32144             return pos;
32145         }
32146         
32147         var rand = Math.floor(Math.random() * (4 - box[0].y));
32148         
32149         pos.push({
32150             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32151             y : minY + (this.unitWidth + this.gutter) * rand
32152         });
32153         
32154         return pos;
32155         
32156     },
32157     
32158     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
32159     {
32160         var pos = [];
32161         
32162         if(box[0].size == 'xs'){
32163             
32164             pos.push({
32165                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32166                 y : minY
32167             });
32168
32169             pos.push({
32170                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32171                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
32172             });
32173             
32174             return pos;
32175             
32176         }
32177         
32178         pos.push({
32179             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32180             y : minY
32181         });
32182
32183         pos.push({
32184             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32185             y : minY + (this.unitWidth + this.gutter) * 2
32186         });
32187         
32188         return pos;
32189         
32190     },
32191     
32192     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
32193     {
32194         var pos = [];
32195         
32196         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
32197             
32198             pos.push({
32199                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32200                 y : minY
32201             });
32202
32203             pos.push({
32204                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32205                 y : minY + (this.unitWidth + this.gutter) * 1
32206             });
32207             
32208             pos.push({
32209                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32210                 y : minY + (this.unitWidth + this.gutter) * 2
32211             });
32212             
32213             return pos;
32214             
32215         }
32216         
32217         if(box[0].size == 'xs' && box[1].size == 'xs'){
32218             
32219             pos.push({
32220                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32221                 y : minY
32222             });
32223
32224             pos.push({
32225                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32226                 y : minY
32227             });
32228             
32229             pos.push({
32230                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32231                 y : minY + (this.unitWidth + this.gutter) * 1
32232             });
32233             
32234             return pos;
32235             
32236         }
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) * 2
32246         });
32247
32248         pos.push({
32249             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - 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     getHorizontalFourBoxColPositions : function(maxX, minY, box)
32258     {
32259         var pos = [];
32260         
32261         if(box[0].size == 'xs'){
32262             
32263             pos.push({
32264                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32265                 y : minY
32266             });
32267
32268             pos.push({
32269                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32270                 y : minY
32271             });
32272             
32273             pos.push({
32274                 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),
32275                 y : minY
32276             });
32277             
32278             pos.push({
32279                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
32280                 y : minY + (this.unitWidth + this.gutter) * 1
32281             });
32282             
32283             return pos;
32284             
32285         }
32286         
32287         pos.push({
32288             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
32289             y : minY
32290         });
32291         
32292         pos.push({
32293             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
32294             y : minY + (this.unitWidth + this.gutter) * 2
32295         });
32296         
32297         pos.push({
32298             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
32299             y : minY + (this.unitWidth + this.gutter) * 2
32300         });
32301         
32302         pos.push({
32303             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),
32304             y : minY + (this.unitWidth + this.gutter) * 2
32305         });
32306
32307         return pos;
32308         
32309     },
32310     
32311     /**
32312     * remove a Masonry Brick
32313     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
32314     */
32315     removeBrick : function(brick_id)
32316     {
32317         if (!brick_id) {
32318             return;
32319         }
32320         
32321         for (var i = 0; i<this.bricks.length; i++) {
32322             if (this.bricks[i].id == brick_id) {
32323                 this.bricks.splice(i,1);
32324                 this.el.dom.removeChild(Roo.get(brick_id).dom);
32325                 this.initial();
32326             }
32327         }
32328     },
32329     
32330     /**
32331     * adds a Masonry Brick
32332     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32333     */
32334     addBrick : function(cfg)
32335     {
32336         var cn = new Roo.bootstrap.MasonryBrick(cfg);
32337         //this.register(cn);
32338         cn.parentId = this.id;
32339         cn.render(this.el);
32340         return cn;
32341     },
32342     
32343     /**
32344     * register a Masonry Brick
32345     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32346     */
32347     
32348     register : function(brick)
32349     {
32350         this.bricks.push(brick);
32351         brick.masonryId = this.id;
32352     },
32353     
32354     /**
32355     * clear all the Masonry Brick
32356     */
32357     clearAll : function()
32358     {
32359         this.bricks = [];
32360         //this.getChildContainer().dom.innerHTML = "";
32361         this.el.dom.innerHTML = '';
32362     },
32363     
32364     getSelected : function()
32365     {
32366         if (!this.selectedBrick) {
32367             return false;
32368         }
32369         
32370         return this.selectedBrick;
32371     }
32372 });
32373
32374 Roo.apply(Roo.bootstrap.LayoutMasonry, {
32375     
32376     groups: {},
32377      /**
32378     * register a Masonry Layout
32379     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
32380     */
32381     
32382     register : function(layout)
32383     {
32384         this.groups[layout.id] = layout;
32385     },
32386     /**
32387     * fetch a  Masonry Layout based on the masonry layout ID
32388     * @param {string} the masonry layout to add
32389     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
32390     */
32391     
32392     get: function(layout_id) {
32393         if (typeof(this.groups[layout_id]) == 'undefined') {
32394             return false;
32395         }
32396         return this.groups[layout_id] ;
32397     }
32398     
32399     
32400     
32401 });
32402
32403  
32404
32405  /**
32406  *
32407  * This is based on 
32408  * http://masonry.desandro.com
32409  *
32410  * The idea is to render all the bricks based on vertical width...
32411  *
32412  * The original code extends 'outlayer' - we might need to use that....
32413  * 
32414  */
32415
32416
32417 /**
32418  * @class Roo.bootstrap.LayoutMasonryAuto
32419  * @extends Roo.bootstrap.Component
32420  * Bootstrap Layout Masonry class
32421  * 
32422  * @constructor
32423  * Create a new Element
32424  * @param {Object} config The config object
32425  */
32426
32427 Roo.bootstrap.LayoutMasonryAuto = function(config){
32428     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
32429 };
32430
32431 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
32432     
32433       /**
32434      * @cfg {Boolean} isFitWidth  - resize the width..
32435      */   
32436     isFitWidth : false,  // options..
32437     /**
32438      * @cfg {Boolean} isOriginLeft = left align?
32439      */   
32440     isOriginLeft : true,
32441     /**
32442      * @cfg {Boolean} isOriginTop = top align?
32443      */   
32444     isOriginTop : false,
32445     /**
32446      * @cfg {Boolean} isLayoutInstant = no animation?
32447      */   
32448     isLayoutInstant : false, // needed?
32449     /**
32450      * @cfg {Boolean} isResizingContainer = not sure if this is used..
32451      */   
32452     isResizingContainer : true,
32453     /**
32454      * @cfg {Number} columnWidth  width of the columns 
32455      */   
32456     
32457     columnWidth : 0,
32458     
32459     /**
32460      * @cfg {Number} maxCols maximum number of columns
32461      */   
32462     
32463     maxCols: 0,
32464     /**
32465      * @cfg {Number} padHeight padding below box..
32466      */   
32467     
32468     padHeight : 10, 
32469     
32470     /**
32471      * @cfg {Boolean} isAutoInitial defalut true
32472      */   
32473     
32474     isAutoInitial : true, 
32475     
32476     // private?
32477     gutter : 0,
32478     
32479     containerWidth: 0,
32480     initialColumnWidth : 0,
32481     currentSize : null,
32482     
32483     colYs : null, // array.
32484     maxY : 0,
32485     padWidth: 10,
32486     
32487     
32488     tag: 'div',
32489     cls: '',
32490     bricks: null, //CompositeElement
32491     cols : 0, // array?
32492     // element : null, // wrapped now this.el
32493     _isLayoutInited : null, 
32494     
32495     
32496     getAutoCreate : function(){
32497         
32498         var cfg = {
32499             tag: this.tag,
32500             cls: 'blog-masonary-wrapper ' + this.cls,
32501             cn : {
32502                 cls : 'mas-boxes masonary'
32503             }
32504         };
32505         
32506         return cfg;
32507     },
32508     
32509     getChildContainer: function( )
32510     {
32511         if (this.boxesEl) {
32512             return this.boxesEl;
32513         }
32514         
32515         this.boxesEl = this.el.select('.mas-boxes').first();
32516         
32517         return this.boxesEl;
32518     },
32519     
32520     
32521     initEvents : function()
32522     {
32523         var _this = this;
32524         
32525         if(this.isAutoInitial){
32526             Roo.log('hook children rendered');
32527             this.on('childrenrendered', function() {
32528                 Roo.log('children rendered');
32529                 _this.initial();
32530             } ,this);
32531         }
32532         
32533     },
32534     
32535     initial : function()
32536     {
32537         this.reloadItems();
32538
32539         this.currentSize = this.el.getBox(true);
32540
32541         /// was window resize... - let's see if this works..
32542         Roo.EventManager.onWindowResize(this.resize, this); 
32543
32544         if(!this.isAutoInitial){
32545             this.layout();
32546             return;
32547         }
32548         
32549         this.layout.defer(500,this);
32550     },
32551     
32552     reloadItems: function()
32553     {
32554         this.bricks = this.el.select('.masonry-brick', true);
32555         
32556         this.bricks.each(function(b) {
32557             //Roo.log(b.getSize());
32558             if (!b.attr('originalwidth')) {
32559                 b.attr('originalwidth',  b.getSize().width);
32560             }
32561             
32562         });
32563         
32564         Roo.log(this.bricks.elements.length);
32565     },
32566     
32567     resize : function()
32568     {
32569         Roo.log('resize');
32570         var cs = this.el.getBox(true);
32571         
32572         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
32573             Roo.log("no change in with or X");
32574             return;
32575         }
32576         this.currentSize = cs;
32577         this.layout();
32578     },
32579     
32580     layout : function()
32581     {
32582          Roo.log('layout');
32583         this._resetLayout();
32584         //this._manageStamps();
32585       
32586         // don't animate first layout
32587         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
32588         this.layoutItems( isInstant );
32589       
32590         // flag for initalized
32591         this._isLayoutInited = true;
32592     },
32593     
32594     layoutItems : function( isInstant )
32595     {
32596         //var items = this._getItemsForLayout( this.items );
32597         // original code supports filtering layout items.. we just ignore it..
32598         
32599         this._layoutItems( this.bricks , isInstant );
32600       
32601         this._postLayout();
32602     },
32603     _layoutItems : function ( items , isInstant)
32604     {
32605        //this.fireEvent( 'layout', this, items );
32606     
32607
32608         if ( !items || !items.elements.length ) {
32609           // no items, emit event with empty array
32610             return;
32611         }
32612
32613         var queue = [];
32614         items.each(function(item) {
32615             Roo.log("layout item");
32616             Roo.log(item);
32617             // get x/y object from method
32618             var position = this._getItemLayoutPosition( item );
32619             // enqueue
32620             position.item = item;
32621             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
32622             queue.push( position );
32623         }, this);
32624       
32625         this._processLayoutQueue( queue );
32626     },
32627     /** Sets position of item in DOM
32628     * @param {Element} item
32629     * @param {Number} x - horizontal position
32630     * @param {Number} y - vertical position
32631     * @param {Boolean} isInstant - disables transitions
32632     */
32633     _processLayoutQueue : function( queue )
32634     {
32635         for ( var i=0, len = queue.length; i < len; i++ ) {
32636             var obj = queue[i];
32637             obj.item.position('absolute');
32638             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
32639         }
32640     },
32641       
32642     
32643     /**
32644     * Any logic you want to do after each layout,
32645     * i.e. size the container
32646     */
32647     _postLayout : function()
32648     {
32649         this.resizeContainer();
32650     },
32651     
32652     resizeContainer : function()
32653     {
32654         if ( !this.isResizingContainer ) {
32655             return;
32656         }
32657         var size = this._getContainerSize();
32658         if ( size ) {
32659             this.el.setSize(size.width,size.height);
32660             this.boxesEl.setSize(size.width,size.height);
32661         }
32662     },
32663     
32664     
32665     
32666     _resetLayout : function()
32667     {
32668         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
32669         this.colWidth = this.el.getWidth();
32670         //this.gutter = this.el.getWidth(); 
32671         
32672         this.measureColumns();
32673
32674         // reset column Y
32675         var i = this.cols;
32676         this.colYs = [];
32677         while (i--) {
32678             this.colYs.push( 0 );
32679         }
32680     
32681         this.maxY = 0;
32682     },
32683
32684     measureColumns : function()
32685     {
32686         this.getContainerWidth();
32687       // if columnWidth is 0, default to outerWidth of first item
32688         if ( !this.columnWidth ) {
32689             var firstItem = this.bricks.first();
32690             Roo.log(firstItem);
32691             this.columnWidth  = this.containerWidth;
32692             if (firstItem && firstItem.attr('originalwidth') ) {
32693                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32694             }
32695             // columnWidth fall back to item of first element
32696             Roo.log("set column width?");
32697                         this.initialColumnWidth = this.columnWidth  ;
32698
32699             // if first elem has no width, default to size of container
32700             
32701         }
32702         
32703         
32704         if (this.initialColumnWidth) {
32705             this.columnWidth = this.initialColumnWidth;
32706         }
32707         
32708         
32709             
32710         // column width is fixed at the top - however if container width get's smaller we should
32711         // reduce it...
32712         
32713         // this bit calcs how man columns..
32714             
32715         var columnWidth = this.columnWidth += this.gutter;
32716       
32717         // calculate columns
32718         var containerWidth = this.containerWidth + this.gutter;
32719         
32720         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32721         // fix rounding errors, typically with gutters
32722         var excess = columnWidth - containerWidth % columnWidth;
32723         
32724         
32725         // if overshoot is less than a pixel, round up, otherwise floor it
32726         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32727         cols = Math[ mathMethod ]( cols );
32728         this.cols = Math.max( cols, 1 );
32729         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32730         
32731          // padding positioning..
32732         var totalColWidth = this.cols * this.columnWidth;
32733         var padavail = this.containerWidth - totalColWidth;
32734         // so for 2 columns - we need 3 'pads'
32735         
32736         var padNeeded = (1+this.cols) * this.padWidth;
32737         
32738         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32739         
32740         this.columnWidth += padExtra
32741         //this.padWidth = Math.floor(padavail /  ( this.cols));
32742         
32743         // adjust colum width so that padding is fixed??
32744         
32745         // we have 3 columns ... total = width * 3
32746         // we have X left over... that should be used by 
32747         
32748         //if (this.expandC) {
32749             
32750         //}
32751         
32752         
32753         
32754     },
32755     
32756     getContainerWidth : function()
32757     {
32758        /* // container is parent if fit width
32759         var container = this.isFitWidth ? this.element.parentNode : this.element;
32760         // check that this.size and size are there
32761         // IE8 triggers resize on body size change, so they might not be
32762         
32763         var size = getSize( container );  //FIXME
32764         this.containerWidth = size && size.innerWidth; //FIXME
32765         */
32766          
32767         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32768         
32769     },
32770     
32771     _getItemLayoutPosition : function( item )  // what is item?
32772     {
32773         // we resize the item to our columnWidth..
32774       
32775         item.setWidth(this.columnWidth);
32776         item.autoBoxAdjust  = false;
32777         
32778         var sz = item.getSize();
32779  
32780         // how many columns does this brick span
32781         var remainder = this.containerWidth % this.columnWidth;
32782         
32783         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32784         // round if off by 1 pixel, otherwise use ceil
32785         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32786         colSpan = Math.min( colSpan, this.cols );
32787         
32788         // normally this should be '1' as we dont' currently allow multi width columns..
32789         
32790         var colGroup = this._getColGroup( colSpan );
32791         // get the minimum Y value from the columns
32792         var minimumY = Math.min.apply( Math, colGroup );
32793         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32794         
32795         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32796          
32797         // position the brick
32798         var position = {
32799             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32800             y: this.currentSize.y + minimumY + this.padHeight
32801         };
32802         
32803         Roo.log(position);
32804         // apply setHeight to necessary columns
32805         var setHeight = minimumY + sz.height + this.padHeight;
32806         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32807         
32808         var setSpan = this.cols + 1 - colGroup.length;
32809         for ( var i = 0; i < setSpan; i++ ) {
32810           this.colYs[ shortColIndex + i ] = setHeight ;
32811         }
32812       
32813         return position;
32814     },
32815     
32816     /**
32817      * @param {Number} colSpan - number of columns the element spans
32818      * @returns {Array} colGroup
32819      */
32820     _getColGroup : function( colSpan )
32821     {
32822         if ( colSpan < 2 ) {
32823           // if brick spans only one column, use all the column Ys
32824           return this.colYs;
32825         }
32826       
32827         var colGroup = [];
32828         // how many different places could this brick fit horizontally
32829         var groupCount = this.cols + 1 - colSpan;
32830         // for each group potential horizontal position
32831         for ( var i = 0; i < groupCount; i++ ) {
32832           // make an array of colY values for that one group
32833           var groupColYs = this.colYs.slice( i, i + colSpan );
32834           // and get the max value of the array
32835           colGroup[i] = Math.max.apply( Math, groupColYs );
32836         }
32837         return colGroup;
32838     },
32839     /*
32840     _manageStamp : function( stamp )
32841     {
32842         var stampSize =  stamp.getSize();
32843         var offset = stamp.getBox();
32844         // get the columns that this stamp affects
32845         var firstX = this.isOriginLeft ? offset.x : offset.right;
32846         var lastX = firstX + stampSize.width;
32847         var firstCol = Math.floor( firstX / this.columnWidth );
32848         firstCol = Math.max( 0, firstCol );
32849         
32850         var lastCol = Math.floor( lastX / this.columnWidth );
32851         // lastCol should not go over if multiple of columnWidth #425
32852         lastCol -= lastX % this.columnWidth ? 0 : 1;
32853         lastCol = Math.min( this.cols - 1, lastCol );
32854         
32855         // set colYs to bottom of the stamp
32856         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32857             stampSize.height;
32858             
32859         for ( var i = firstCol; i <= lastCol; i++ ) {
32860           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32861         }
32862     },
32863     */
32864     
32865     _getContainerSize : function()
32866     {
32867         this.maxY = Math.max.apply( Math, this.colYs );
32868         var size = {
32869             height: this.maxY
32870         };
32871       
32872         if ( this.isFitWidth ) {
32873             size.width = this._getContainerFitWidth();
32874         }
32875       
32876         return size;
32877     },
32878     
32879     _getContainerFitWidth : function()
32880     {
32881         var unusedCols = 0;
32882         // count unused columns
32883         var i = this.cols;
32884         while ( --i ) {
32885           if ( this.colYs[i] !== 0 ) {
32886             break;
32887           }
32888           unusedCols++;
32889         }
32890         // fit container to columns that have been used
32891         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32892     },
32893     
32894     needsResizeLayout : function()
32895     {
32896         var previousWidth = this.containerWidth;
32897         this.getContainerWidth();
32898         return previousWidth !== this.containerWidth;
32899     }
32900  
32901 });
32902
32903  
32904
32905  /*
32906  * - LGPL
32907  *
32908  * element
32909  * 
32910  */
32911
32912 /**
32913  * @class Roo.bootstrap.MasonryBrick
32914  * @extends Roo.bootstrap.Component
32915  * Bootstrap MasonryBrick class
32916  * 
32917  * @constructor
32918  * Create a new MasonryBrick
32919  * @param {Object} config The config object
32920  */
32921
32922 Roo.bootstrap.MasonryBrick = function(config){
32923     
32924     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32925     
32926     Roo.bootstrap.MasonryBrick.register(this);
32927     
32928     this.addEvents({
32929         // raw events
32930         /**
32931          * @event click
32932          * When a MasonryBrick is clcik
32933          * @param {Roo.bootstrap.MasonryBrick} this
32934          * @param {Roo.EventObject} e
32935          */
32936         "click" : true
32937     });
32938 };
32939
32940 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32941     
32942     /**
32943      * @cfg {String} title
32944      */   
32945     title : '',
32946     /**
32947      * @cfg {String} html
32948      */   
32949     html : '',
32950     /**
32951      * @cfg {String} bgimage
32952      */   
32953     bgimage : '',
32954     /**
32955      * @cfg {String} videourl
32956      */   
32957     videourl : '',
32958     /**
32959      * @cfg {String} cls
32960      */   
32961     cls : '',
32962     /**
32963      * @cfg {String} href
32964      */   
32965     href : '',
32966     /**
32967      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32968      */   
32969     size : 'xs',
32970     
32971     /**
32972      * @cfg {String} placetitle (center|bottom)
32973      */   
32974     placetitle : '',
32975     
32976     /**
32977      * @cfg {Boolean} isFitContainer defalut true
32978      */   
32979     isFitContainer : true, 
32980     
32981     /**
32982      * @cfg {Boolean} preventDefault defalut false
32983      */   
32984     preventDefault : false, 
32985     
32986     /**
32987      * @cfg {Boolean} inverse defalut false
32988      */   
32989     maskInverse : false, 
32990     
32991     getAutoCreate : function()
32992     {
32993         if(!this.isFitContainer){
32994             return this.getSplitAutoCreate();
32995         }
32996         
32997         var cls = 'masonry-brick masonry-brick-full';
32998         
32999         if(this.href.length){
33000             cls += ' masonry-brick-link';
33001         }
33002         
33003         if(this.bgimage.length){
33004             cls += ' masonry-brick-image';
33005         }
33006         
33007         if(this.maskInverse){
33008             cls += ' mask-inverse';
33009         }
33010         
33011         if(!this.html.length && !this.maskInverse && !this.videourl.length){
33012             cls += ' enable-mask';
33013         }
33014         
33015         if(this.size){
33016             cls += ' masonry-' + this.size + '-brick';
33017         }
33018         
33019         if(this.placetitle.length){
33020             
33021             switch (this.placetitle) {
33022                 case 'center' :
33023                     cls += ' masonry-center-title';
33024                     break;
33025                 case 'bottom' :
33026                     cls += ' masonry-bottom-title';
33027                     break;
33028                 default:
33029                     break;
33030             }
33031             
33032         } else {
33033             if(!this.html.length && !this.bgimage.length){
33034                 cls += ' masonry-center-title';
33035             }
33036
33037             if(!this.html.length && this.bgimage.length){
33038                 cls += ' masonry-bottom-title';
33039             }
33040         }
33041         
33042         if(this.cls){
33043             cls += ' ' + this.cls;
33044         }
33045         
33046         var cfg = {
33047             tag: (this.href.length) ? 'a' : 'div',
33048             cls: cls,
33049             cn: [
33050                 {
33051                     tag: 'div',
33052                     cls: 'masonry-brick-mask'
33053                 },
33054                 {
33055                     tag: 'div',
33056                     cls: 'masonry-brick-paragraph',
33057                     cn: []
33058                 }
33059             ]
33060         };
33061         
33062         if(this.href.length){
33063             cfg.href = this.href;
33064         }
33065         
33066         var cn = cfg.cn[1].cn;
33067         
33068         if(this.title.length){
33069             cn.push({
33070                 tag: 'h4',
33071                 cls: 'masonry-brick-title',
33072                 html: this.title
33073             });
33074         }
33075         
33076         if(this.html.length){
33077             cn.push({
33078                 tag: 'p',
33079                 cls: 'masonry-brick-text',
33080                 html: this.html
33081             });
33082         }
33083         
33084         if (!this.title.length && !this.html.length) {
33085             cfg.cn[1].cls += ' hide';
33086         }
33087         
33088         if(this.bgimage.length){
33089             cfg.cn.push({
33090                 tag: 'img',
33091                 cls: 'masonry-brick-image-view',
33092                 src: this.bgimage
33093             });
33094         }
33095         
33096         if(this.videourl.length){
33097             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33098             // youtube support only?
33099             cfg.cn.push({
33100                 tag: 'iframe',
33101                 cls: 'masonry-brick-image-view',
33102                 src: vurl,
33103                 frameborder : 0,
33104                 allowfullscreen : true
33105             });
33106         }
33107         
33108         return cfg;
33109         
33110     },
33111     
33112     getSplitAutoCreate : function()
33113     {
33114         var cls = 'masonry-brick masonry-brick-split';
33115         
33116         if(this.href.length){
33117             cls += ' masonry-brick-link';
33118         }
33119         
33120         if(this.bgimage.length){
33121             cls += ' masonry-brick-image';
33122         }
33123         
33124         if(this.size){
33125             cls += ' masonry-' + this.size + '-brick';
33126         }
33127         
33128         switch (this.placetitle) {
33129             case 'center' :
33130                 cls += ' masonry-center-title';
33131                 break;
33132             case 'bottom' :
33133                 cls += ' masonry-bottom-title';
33134                 break;
33135             default:
33136                 if(!this.bgimage.length){
33137                     cls += ' masonry-center-title';
33138                 }
33139
33140                 if(this.bgimage.length){
33141                     cls += ' masonry-bottom-title';
33142                 }
33143                 break;
33144         }
33145         
33146         if(this.cls){
33147             cls += ' ' + this.cls;
33148         }
33149         
33150         var cfg = {
33151             tag: (this.href.length) ? 'a' : 'div',
33152             cls: cls,
33153             cn: [
33154                 {
33155                     tag: 'div',
33156                     cls: 'masonry-brick-split-head',
33157                     cn: [
33158                         {
33159                             tag: 'div',
33160                             cls: 'masonry-brick-paragraph',
33161                             cn: []
33162                         }
33163                     ]
33164                 },
33165                 {
33166                     tag: 'div',
33167                     cls: 'masonry-brick-split-body',
33168                     cn: []
33169                 }
33170             ]
33171         };
33172         
33173         if(this.href.length){
33174             cfg.href = this.href;
33175         }
33176         
33177         if(this.title.length){
33178             cfg.cn[0].cn[0].cn.push({
33179                 tag: 'h4',
33180                 cls: 'masonry-brick-title',
33181                 html: this.title
33182             });
33183         }
33184         
33185         if(this.html.length){
33186             cfg.cn[1].cn.push({
33187                 tag: 'p',
33188                 cls: 'masonry-brick-text',
33189                 html: this.html
33190             });
33191         }
33192
33193         if(this.bgimage.length){
33194             cfg.cn[0].cn.push({
33195                 tag: 'img',
33196                 cls: 'masonry-brick-image-view',
33197                 src: this.bgimage
33198             });
33199         }
33200         
33201         if(this.videourl.length){
33202             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
33203             // youtube support only?
33204             cfg.cn[0].cn.cn.push({
33205                 tag: 'iframe',
33206                 cls: 'masonry-brick-image-view',
33207                 src: vurl,
33208                 frameborder : 0,
33209                 allowfullscreen : true
33210             });
33211         }
33212         
33213         return cfg;
33214     },
33215     
33216     initEvents: function() 
33217     {
33218         switch (this.size) {
33219             case 'xs' :
33220                 this.x = 1;
33221                 this.y = 1;
33222                 break;
33223             case 'sm' :
33224                 this.x = 2;
33225                 this.y = 2;
33226                 break;
33227             case 'md' :
33228             case 'md-left' :
33229             case 'md-right' :
33230                 this.x = 3;
33231                 this.y = 3;
33232                 break;
33233             case 'tall' :
33234                 this.x = 2;
33235                 this.y = 3;
33236                 break;
33237             case 'wide' :
33238                 this.x = 3;
33239                 this.y = 2;
33240                 break;
33241             case 'wide-thin' :
33242                 this.x = 3;
33243                 this.y = 1;
33244                 break;
33245                         
33246             default :
33247                 break;
33248         }
33249         
33250         if(Roo.isTouch){
33251             this.el.on('touchstart', this.onTouchStart, this);
33252             this.el.on('touchmove', this.onTouchMove, this);
33253             this.el.on('touchend', this.onTouchEnd, this);
33254             this.el.on('contextmenu', this.onContextMenu, this);
33255         } else {
33256             this.el.on('mouseenter'  ,this.enter, this);
33257             this.el.on('mouseleave', this.leave, this);
33258             this.el.on('click', this.onClick, this);
33259         }
33260         
33261         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
33262             this.parent().bricks.push(this);   
33263         }
33264         
33265     },
33266     
33267     onClick: function(e, el)
33268     {
33269         var time = this.endTimer - this.startTimer;
33270         // Roo.log(e.preventDefault());
33271         if(Roo.isTouch){
33272             if(time > 1000){
33273                 e.preventDefault();
33274                 return;
33275             }
33276         }
33277         
33278         if(!this.preventDefault){
33279             return;
33280         }
33281         
33282         e.preventDefault();
33283         
33284         if (this.activeClass != '') {
33285             this.selectBrick();
33286         }
33287         
33288         this.fireEvent('click', this, e);
33289     },
33290     
33291     enter: function(e, el)
33292     {
33293         e.preventDefault();
33294         
33295         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
33296             return;
33297         }
33298         
33299         if(this.bgimage.length && this.html.length){
33300             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33301         }
33302     },
33303     
33304     leave: function(e, el)
33305     {
33306         e.preventDefault();
33307         
33308         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
33309             return;
33310         }
33311         
33312         if(this.bgimage.length && this.html.length){
33313             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33314         }
33315     },
33316     
33317     onTouchStart: function(e, el)
33318     {
33319 //        e.preventDefault();
33320         
33321         this.touchmoved = false;
33322         
33323         if(!this.isFitContainer){
33324             return;
33325         }
33326         
33327         if(!this.bgimage.length || !this.html.length){
33328             return;
33329         }
33330         
33331         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
33332         
33333         this.timer = new Date().getTime();
33334         
33335     },
33336     
33337     onTouchMove: function(e, el)
33338     {
33339         this.touchmoved = true;
33340     },
33341     
33342     onContextMenu : function(e,el)
33343     {
33344         e.preventDefault();
33345         e.stopPropagation();
33346         return false;
33347     },
33348     
33349     onTouchEnd: function(e, el)
33350     {
33351 //        e.preventDefault();
33352         
33353         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
33354         
33355             this.leave(e,el);
33356             
33357             return;
33358         }
33359         
33360         if(!this.bgimage.length || !this.html.length){
33361             
33362             if(this.href.length){
33363                 window.location.href = this.href;
33364             }
33365             
33366             return;
33367         }
33368         
33369         if(!this.isFitContainer){
33370             return;
33371         }
33372         
33373         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
33374         
33375         window.location.href = this.href;
33376     },
33377     
33378     //selection on single brick only
33379     selectBrick : function() {
33380         
33381         if (!this.parentId) {
33382             return;
33383         }
33384         
33385         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
33386         var index = m.selectedBrick.indexOf(this.id);
33387         
33388         if ( index > -1) {
33389             m.selectedBrick.splice(index,1);
33390             this.el.removeClass(this.activeClass);
33391             return;
33392         }
33393         
33394         for(var i = 0; i < m.selectedBrick.length; i++) {
33395             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
33396             b.el.removeClass(b.activeClass);
33397         }
33398         
33399         m.selectedBrick = [];
33400         
33401         m.selectedBrick.push(this.id);
33402         this.el.addClass(this.activeClass);
33403         return;
33404     },
33405     
33406     isSelected : function(){
33407         return this.el.hasClass(this.activeClass);
33408         
33409     }
33410 });
33411
33412 Roo.apply(Roo.bootstrap.MasonryBrick, {
33413     
33414     //groups: {},
33415     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
33416      /**
33417     * register a Masonry Brick
33418     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
33419     */
33420     
33421     register : function(brick)
33422     {
33423         //this.groups[brick.id] = brick;
33424         this.groups.add(brick.id, brick);
33425     },
33426     /**
33427     * fetch a  masonry brick based on the masonry brick ID
33428     * @param {string} the masonry brick to add
33429     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
33430     */
33431     
33432     get: function(brick_id) 
33433     {
33434         // if (typeof(this.groups[brick_id]) == 'undefined') {
33435         //     return false;
33436         // }
33437         // return this.groups[brick_id] ;
33438         
33439         if(this.groups.key(brick_id)) {
33440             return this.groups.key(brick_id);
33441         }
33442         
33443         return false;
33444     }
33445     
33446     
33447     
33448 });
33449
33450  /*
33451  * - LGPL
33452  *
33453  * element
33454  * 
33455  */
33456
33457 /**
33458  * @class Roo.bootstrap.Brick
33459  * @extends Roo.bootstrap.Component
33460  * Bootstrap Brick class
33461  * 
33462  * @constructor
33463  * Create a new Brick
33464  * @param {Object} config The config object
33465  */
33466
33467 Roo.bootstrap.Brick = function(config){
33468     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
33469     
33470     this.addEvents({
33471         // raw events
33472         /**
33473          * @event click
33474          * When a Brick is click
33475          * @param {Roo.bootstrap.Brick} this
33476          * @param {Roo.EventObject} e
33477          */
33478         "click" : true
33479     });
33480 };
33481
33482 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
33483     
33484     /**
33485      * @cfg {String} title
33486      */   
33487     title : '',
33488     /**
33489      * @cfg {String} html
33490      */   
33491     html : '',
33492     /**
33493      * @cfg {String} bgimage
33494      */   
33495     bgimage : '',
33496     /**
33497      * @cfg {String} cls
33498      */   
33499     cls : '',
33500     /**
33501      * @cfg {String} href
33502      */   
33503     href : '',
33504     /**
33505      * @cfg {String} video
33506      */   
33507     video : '',
33508     /**
33509      * @cfg {Boolean} square
33510      */   
33511     square : true,
33512     
33513     getAutoCreate : function()
33514     {
33515         var cls = 'roo-brick';
33516         
33517         if(this.href.length){
33518             cls += ' roo-brick-link';
33519         }
33520         
33521         if(this.bgimage.length){
33522             cls += ' roo-brick-image';
33523         }
33524         
33525         if(!this.html.length && !this.bgimage.length){
33526             cls += ' roo-brick-center-title';
33527         }
33528         
33529         if(!this.html.length && this.bgimage.length){
33530             cls += ' roo-brick-bottom-title';
33531         }
33532         
33533         if(this.cls){
33534             cls += ' ' + this.cls;
33535         }
33536         
33537         var cfg = {
33538             tag: (this.href.length) ? 'a' : 'div',
33539             cls: cls,
33540             cn: [
33541                 {
33542                     tag: 'div',
33543                     cls: 'roo-brick-paragraph',
33544                     cn: []
33545                 }
33546             ]
33547         };
33548         
33549         if(this.href.length){
33550             cfg.href = this.href;
33551         }
33552         
33553         var cn = cfg.cn[0].cn;
33554         
33555         if(this.title.length){
33556             cn.push({
33557                 tag: 'h4',
33558                 cls: 'roo-brick-title',
33559                 html: this.title
33560             });
33561         }
33562         
33563         if(this.html.length){
33564             cn.push({
33565                 tag: 'p',
33566                 cls: 'roo-brick-text',
33567                 html: this.html
33568             });
33569         } else {
33570             cn.cls += ' hide';
33571         }
33572         
33573         if(this.bgimage.length){
33574             cfg.cn.push({
33575                 tag: 'img',
33576                 cls: 'roo-brick-image-view',
33577                 src: this.bgimage
33578             });
33579         }
33580         
33581         return cfg;
33582     },
33583     
33584     initEvents: function() 
33585     {
33586         if(this.title.length || this.html.length){
33587             this.el.on('mouseenter'  ,this.enter, this);
33588             this.el.on('mouseleave', this.leave, this);
33589         }
33590         
33591         Roo.EventManager.onWindowResize(this.resize, this); 
33592         
33593         if(this.bgimage.length){
33594             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
33595             this.imageEl.on('load', this.onImageLoad, this);
33596             return;
33597         }
33598         
33599         this.resize();
33600     },
33601     
33602     onImageLoad : function()
33603     {
33604         this.resize();
33605     },
33606     
33607     resize : function()
33608     {
33609         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
33610         
33611         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
33612         
33613         if(this.bgimage.length){
33614             var image = this.el.select('.roo-brick-image-view', true).first();
33615             
33616             image.setWidth(paragraph.getWidth());
33617             
33618             if(this.square){
33619                 image.setHeight(paragraph.getWidth());
33620             }
33621             
33622             this.el.setHeight(image.getHeight());
33623             paragraph.setHeight(image.getHeight());
33624             
33625         }
33626         
33627     },
33628     
33629     enter: function(e, el)
33630     {
33631         e.preventDefault();
33632         
33633         if(this.bgimage.length){
33634             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
33635             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
33636         }
33637     },
33638     
33639     leave: function(e, el)
33640     {
33641         e.preventDefault();
33642         
33643         if(this.bgimage.length){
33644             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
33645             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
33646         }
33647     }
33648     
33649 });
33650
33651  
33652
33653  /*
33654  * - LGPL
33655  *
33656  * Number field 
33657  */
33658
33659 /**
33660  * @class Roo.bootstrap.NumberField
33661  * @extends Roo.bootstrap.Input
33662  * Bootstrap NumberField class
33663  * 
33664  * 
33665  * 
33666  * 
33667  * @constructor
33668  * Create a new NumberField
33669  * @param {Object} config The config object
33670  */
33671
33672 Roo.bootstrap.NumberField = function(config){
33673     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
33674 };
33675
33676 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
33677     
33678     /**
33679      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33680      */
33681     allowDecimals : true,
33682     /**
33683      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33684      */
33685     decimalSeparator : ".",
33686     /**
33687      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33688      */
33689     decimalPrecision : 2,
33690     /**
33691      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33692      */
33693     allowNegative : true,
33694     
33695     /**
33696      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
33697      */
33698     allowZero: true,
33699     /**
33700      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33701      */
33702     minValue : Number.NEGATIVE_INFINITY,
33703     /**
33704      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33705      */
33706     maxValue : Number.MAX_VALUE,
33707     /**
33708      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33709      */
33710     minText : "The minimum value for this field is {0}",
33711     /**
33712      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33713      */
33714     maxText : "The maximum value for this field is {0}",
33715     /**
33716      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33717      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33718      */
33719     nanText : "{0} is not a valid number",
33720     /**
33721      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33722      */
33723     thousandsDelimiter : false,
33724     /**
33725      * @cfg {String} valueAlign alignment of value
33726      */
33727     valueAlign : "left",
33728
33729     getAutoCreate : function()
33730     {
33731         var hiddenInput = {
33732             tag: 'input',
33733             type: 'hidden',
33734             id: Roo.id(),
33735             cls: 'hidden-number-input'
33736         };
33737         
33738         if (this.name) {
33739             hiddenInput.name = this.name;
33740         }
33741         
33742         this.name = '';
33743         
33744         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33745         
33746         this.name = hiddenInput.name;
33747         
33748         if(cfg.cn.length > 0) {
33749             cfg.cn.push(hiddenInput);
33750         }
33751         
33752         return cfg;
33753     },
33754
33755     // private
33756     initEvents : function()
33757     {   
33758         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33759         
33760         var allowed = "0123456789";
33761         
33762         if(this.allowDecimals){
33763             allowed += this.decimalSeparator;
33764         }
33765         
33766         if(this.allowNegative){
33767             allowed += "-";
33768         }
33769         
33770         if(this.thousandsDelimiter) {
33771             allowed += ",";
33772         }
33773         
33774         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33775         
33776         var keyPress = function(e){
33777             
33778             var k = e.getKey();
33779             
33780             var c = e.getCharCode();
33781             
33782             if(
33783                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33784                     allowed.indexOf(String.fromCharCode(c)) === -1
33785             ){
33786                 e.stopEvent();
33787                 return;
33788             }
33789             
33790             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33791                 return;
33792             }
33793             
33794             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33795                 e.stopEvent();
33796             }
33797         };
33798         
33799         this.el.on("keypress", keyPress, this);
33800     },
33801     
33802     validateValue : function(value)
33803     {
33804         
33805         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33806             return false;
33807         }
33808         
33809         var num = this.parseValue(value);
33810         
33811         if(isNaN(num)){
33812             this.markInvalid(String.format(this.nanText, value));
33813             return false;
33814         }
33815         
33816         if(num < this.minValue){
33817             this.markInvalid(String.format(this.minText, this.minValue));
33818             return false;
33819         }
33820         
33821         if(num > this.maxValue){
33822             this.markInvalid(String.format(this.maxText, this.maxValue));
33823             return false;
33824         }
33825         
33826         return true;
33827     },
33828
33829     getValue : function()
33830     {
33831         var v = this.hiddenEl().getValue();
33832         
33833         return this.fixPrecision(this.parseValue(v));
33834     },
33835
33836     parseValue : function(value)
33837     {
33838         if(this.thousandsDelimiter) {
33839             value += "";
33840             r = new RegExp(",", "g");
33841             value = value.replace(r, "");
33842         }
33843         
33844         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33845         return isNaN(value) ? '' : value;
33846     },
33847
33848     fixPrecision : function(value)
33849     {
33850         if(this.thousandsDelimiter) {
33851             value += "";
33852             r = new RegExp(",", "g");
33853             value = value.replace(r, "");
33854         }
33855         
33856         var nan = isNaN(value);
33857         
33858         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33859             return nan ? '' : value;
33860         }
33861         return parseFloat(value).toFixed(this.decimalPrecision);
33862     },
33863
33864     setValue : function(v)
33865     {
33866         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33867         
33868         this.value = v;
33869         
33870         if(this.rendered){
33871             
33872             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33873             
33874             this.inputEl().dom.value = (v == '') ? '' :
33875                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
33876             
33877             if(!this.allowZero && v === '0') {
33878                 this.hiddenEl().dom.value = '';
33879                 this.inputEl().dom.value = '';
33880             }
33881             
33882             this.validate();
33883         }
33884     },
33885
33886     decimalPrecisionFcn : function(v)
33887     {
33888         return Math.floor(v);
33889     },
33890
33891     beforeBlur : function()
33892     {
33893         var v = this.parseValue(this.getRawValue());
33894         
33895         if(v || v === 0 || v === ''){
33896             this.setValue(v);
33897         }
33898     },
33899     
33900     hiddenEl : function()
33901     {
33902         return this.el.select('input.hidden-number-input',true).first();
33903     }
33904     
33905 });
33906
33907  
33908
33909 /*
33910 * Licence: LGPL
33911 */
33912
33913 /**
33914  * @class Roo.bootstrap.DocumentSlider
33915  * @extends Roo.bootstrap.Component
33916  * Bootstrap DocumentSlider class
33917  * 
33918  * @constructor
33919  * Create a new DocumentViewer
33920  * @param {Object} config The config object
33921  */
33922
33923 Roo.bootstrap.DocumentSlider = function(config){
33924     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33925     
33926     this.files = [];
33927     
33928     this.addEvents({
33929         /**
33930          * @event initial
33931          * Fire after initEvent
33932          * @param {Roo.bootstrap.DocumentSlider} this
33933          */
33934         "initial" : true,
33935         /**
33936          * @event update
33937          * Fire after update
33938          * @param {Roo.bootstrap.DocumentSlider} this
33939          */
33940         "update" : true,
33941         /**
33942          * @event click
33943          * Fire after click
33944          * @param {Roo.bootstrap.DocumentSlider} this
33945          */
33946         "click" : true
33947     });
33948 };
33949
33950 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33951     
33952     files : false,
33953     
33954     indicator : 0,
33955     
33956     getAutoCreate : function()
33957     {
33958         var cfg = {
33959             tag : 'div',
33960             cls : 'roo-document-slider',
33961             cn : [
33962                 {
33963                     tag : 'div',
33964                     cls : 'roo-document-slider-header',
33965                     cn : [
33966                         {
33967                             tag : 'div',
33968                             cls : 'roo-document-slider-header-title'
33969                         }
33970                     ]
33971                 },
33972                 {
33973                     tag : 'div',
33974                     cls : 'roo-document-slider-body',
33975                     cn : [
33976                         {
33977                             tag : 'div',
33978                             cls : 'roo-document-slider-prev',
33979                             cn : [
33980                                 {
33981                                     tag : 'i',
33982                                     cls : 'fa fa-chevron-left'
33983                                 }
33984                             ]
33985                         },
33986                         {
33987                             tag : 'div',
33988                             cls : 'roo-document-slider-thumb',
33989                             cn : [
33990                                 {
33991                                     tag : 'img',
33992                                     cls : 'roo-document-slider-image'
33993                                 }
33994                             ]
33995                         },
33996                         {
33997                             tag : 'div',
33998                             cls : 'roo-document-slider-next',
33999                             cn : [
34000                                 {
34001                                     tag : 'i',
34002                                     cls : 'fa fa-chevron-right'
34003                                 }
34004                             ]
34005                         }
34006                     ]
34007                 }
34008             ]
34009         };
34010         
34011         return cfg;
34012     },
34013     
34014     initEvents : function()
34015     {
34016         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
34017         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
34018         
34019         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
34020         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
34021         
34022         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
34023         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
34024         
34025         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
34026         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
34027         
34028         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
34029         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
34030         
34031         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
34032         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34033         
34034         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
34035         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
34036         
34037         this.thumbEl.on('click', this.onClick, this);
34038         
34039         this.prevIndicator.on('click', this.prev, this);
34040         
34041         this.nextIndicator.on('click', this.next, this);
34042         
34043     },
34044     
34045     initial : function()
34046     {
34047         if(this.files.length){
34048             this.indicator = 1;
34049             this.update()
34050         }
34051         
34052         this.fireEvent('initial', this);
34053     },
34054     
34055     update : function()
34056     {
34057         this.imageEl.attr('src', this.files[this.indicator - 1]);
34058         
34059         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
34060         
34061         this.prevIndicator.show();
34062         
34063         if(this.indicator == 1){
34064             this.prevIndicator.hide();
34065         }
34066         
34067         this.nextIndicator.show();
34068         
34069         if(this.indicator == this.files.length){
34070             this.nextIndicator.hide();
34071         }
34072         
34073         this.thumbEl.scrollTo('top');
34074         
34075         this.fireEvent('update', this);
34076     },
34077     
34078     onClick : function(e)
34079     {
34080         e.preventDefault();
34081         
34082         this.fireEvent('click', this);
34083     },
34084     
34085     prev : function(e)
34086     {
34087         e.preventDefault();
34088         
34089         this.indicator = Math.max(1, this.indicator - 1);
34090         
34091         this.update();
34092     },
34093     
34094     next : function(e)
34095     {
34096         e.preventDefault();
34097         
34098         this.indicator = Math.min(this.files.length, this.indicator + 1);
34099         
34100         this.update();
34101     }
34102 });
34103 /*
34104  * - LGPL
34105  *
34106  * RadioSet
34107  *
34108  *
34109  */
34110
34111 /**
34112  * @class Roo.bootstrap.RadioSet
34113  * @extends Roo.bootstrap.Input
34114  * Bootstrap RadioSet class
34115  * @cfg {String} indicatorpos (left|right) default left
34116  * @cfg {Boolean} inline (true|false) inline the element (default true)
34117  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
34118  * @constructor
34119  * Create a new RadioSet
34120  * @param {Object} config The config object
34121  */
34122
34123 Roo.bootstrap.RadioSet = function(config){
34124     
34125     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
34126     
34127     this.radioes = [];
34128     
34129     Roo.bootstrap.RadioSet.register(this);
34130     
34131     this.addEvents({
34132         /**
34133         * @event check
34134         * Fires when the element is checked or unchecked.
34135         * @param {Roo.bootstrap.RadioSet} this This radio
34136         * @param {Roo.bootstrap.Radio} item The checked item
34137         */
34138        check : true,
34139        /**
34140         * @event click
34141         * Fires when the element is click.
34142         * @param {Roo.bootstrap.RadioSet} this This radio set
34143         * @param {Roo.bootstrap.Radio} item The checked item
34144         * @param {Roo.EventObject} e The event object
34145         */
34146        click : true
34147     });
34148     
34149 };
34150
34151 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
34152
34153     radioes : false,
34154     
34155     inline : true,
34156     
34157     weight : '',
34158     
34159     indicatorpos : 'left',
34160     
34161     getAutoCreate : function()
34162     {
34163         var label = {
34164             tag : 'label',
34165             cls : 'roo-radio-set-label',
34166             cn : [
34167                 {
34168                     tag : 'span',
34169                     html : this.fieldLabel
34170                 }
34171             ]
34172         };
34173         if (Roo.bootstrap.version == 3) {
34174             
34175             
34176             if(this.indicatorpos == 'left'){
34177                 label.cn.unshift({
34178                     tag : 'i',
34179                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
34180                     tooltip : 'This field is required'
34181                 });
34182             } else {
34183                 label.cn.push({
34184                     tag : 'i',
34185                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
34186                     tooltip : 'This field is required'
34187                 });
34188             }
34189         }
34190         var items = {
34191             tag : 'div',
34192             cls : 'roo-radio-set-items'
34193         };
34194         
34195         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
34196         
34197         if (align === 'left' && this.fieldLabel.length) {
34198             
34199             items = {
34200                 cls : "roo-radio-set-right", 
34201                 cn: [
34202                     items
34203                 ]
34204             };
34205             
34206             if(this.labelWidth > 12){
34207                 label.style = "width: " + this.labelWidth + 'px';
34208             }
34209             
34210             if(this.labelWidth < 13 && this.labelmd == 0){
34211                 this.labelmd = this.labelWidth;
34212             }
34213             
34214             if(this.labellg > 0){
34215                 label.cls += ' col-lg-' + this.labellg;
34216                 items.cls += ' col-lg-' + (12 - this.labellg);
34217             }
34218             
34219             if(this.labelmd > 0){
34220                 label.cls += ' col-md-' + this.labelmd;
34221                 items.cls += ' col-md-' + (12 - this.labelmd);
34222             }
34223             
34224             if(this.labelsm > 0){
34225                 label.cls += ' col-sm-' + this.labelsm;
34226                 items.cls += ' col-sm-' + (12 - this.labelsm);
34227             }
34228             
34229             if(this.labelxs > 0){
34230                 label.cls += ' col-xs-' + this.labelxs;
34231                 items.cls += ' col-xs-' + (12 - this.labelxs);
34232             }
34233         }
34234         
34235         var cfg = {
34236             tag : 'div',
34237             cls : 'roo-radio-set',
34238             cn : [
34239                 {
34240                     tag : 'input',
34241                     cls : 'roo-radio-set-input',
34242                     type : 'hidden',
34243                     name : this.name,
34244                     value : this.value ? this.value :  ''
34245                 },
34246                 label,
34247                 items
34248             ]
34249         };
34250         
34251         if(this.weight.length){
34252             cfg.cls += ' roo-radio-' + this.weight;
34253         }
34254         
34255         if(this.inline) {
34256             cfg.cls += ' roo-radio-set-inline';
34257         }
34258         
34259         var settings=this;
34260         ['xs','sm','md','lg'].map(function(size){
34261             if (settings[size]) {
34262                 cfg.cls += ' col-' + size + '-' + settings[size];
34263             }
34264         });
34265         
34266         return cfg;
34267         
34268     },
34269
34270     initEvents : function()
34271     {
34272         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
34273         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
34274         
34275         if(!this.fieldLabel.length){
34276             this.labelEl.hide();
34277         }
34278         
34279         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
34280         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
34281         
34282         this.indicator = this.indicatorEl();
34283         
34284         if(this.indicator){
34285             this.indicator.addClass('invisible');
34286         }
34287         
34288         this.originalValue = this.getValue();
34289         
34290     },
34291     
34292     inputEl: function ()
34293     {
34294         return this.el.select('.roo-radio-set-input', true).first();
34295     },
34296     
34297     getChildContainer : function()
34298     {
34299         return this.itemsEl;
34300     },
34301     
34302     register : function(item)
34303     {
34304         this.radioes.push(item);
34305         
34306     },
34307     
34308     validate : function()
34309     {   
34310         if(this.getVisibilityEl().hasClass('hidden')){
34311             return true;
34312         }
34313         
34314         var valid = false;
34315         
34316         Roo.each(this.radioes, function(i){
34317             if(!i.checked){
34318                 return;
34319             }
34320             
34321             valid = true;
34322             return false;
34323         });
34324         
34325         if(this.allowBlank) {
34326             return true;
34327         }
34328         
34329         if(this.disabled || valid){
34330             this.markValid();
34331             return true;
34332         }
34333         
34334         this.markInvalid();
34335         return false;
34336         
34337     },
34338     
34339     markValid : function()
34340     {
34341         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34342             this.indicatorEl().removeClass('visible');
34343             this.indicatorEl().addClass('invisible');
34344         }
34345         
34346         
34347         if (Roo.bootstrap.version == 3) {
34348             this.el.removeClass([this.invalidClass, this.validClass]);
34349             this.el.addClass(this.validClass);
34350         } else {
34351             this.el.removeClass(['is-invalid','is-valid']);
34352             this.el.addClass(['is-valid']);
34353         }
34354         this.fireEvent('valid', this);
34355     },
34356     
34357     markInvalid : function(msg)
34358     {
34359         if(this.allowBlank || this.disabled){
34360             return;
34361         }
34362         
34363         if(this.labelEl.isVisible(true) && this.indicatorEl()){
34364             this.indicatorEl().removeClass('invisible');
34365             this.indicatorEl().addClass('visible');
34366         }
34367         if (Roo.bootstrap.version == 3) {
34368             this.el.removeClass([this.invalidClass, this.validClass]);
34369             this.el.addClass(this.invalidClass);
34370         } else {
34371             this.el.removeClass(['is-invalid','is-valid']);
34372             this.el.addClass(['is-invalid']);
34373         }
34374         
34375         this.fireEvent('invalid', this, msg);
34376         
34377     },
34378     
34379     setValue : function(v, suppressEvent)
34380     {   
34381         if(this.value === v){
34382             return;
34383         }
34384         
34385         this.value = v;
34386         
34387         if(this.rendered){
34388             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
34389         }
34390         
34391         Roo.each(this.radioes, function(i){
34392             i.checked = false;
34393             i.el.removeClass('checked');
34394         });
34395         
34396         Roo.each(this.radioes, function(i){
34397             
34398             if(i.value === v || i.value.toString() === v.toString()){
34399                 i.checked = true;
34400                 i.el.addClass('checked');
34401                 
34402                 if(suppressEvent !== true){
34403                     this.fireEvent('check', this, i);
34404                 }
34405                 
34406                 return false;
34407             }
34408             
34409         }, this);
34410         
34411         this.validate();
34412     },
34413     
34414     clearInvalid : function(){
34415         
34416         if(!this.el || this.preventMark){
34417             return;
34418         }
34419         
34420         this.el.removeClass([this.invalidClass]);
34421         
34422         this.fireEvent('valid', this);
34423     }
34424     
34425 });
34426
34427 Roo.apply(Roo.bootstrap.RadioSet, {
34428     
34429     groups: {},
34430     
34431     register : function(set)
34432     {
34433         this.groups[set.name] = set;
34434     },
34435     
34436     get: function(name) 
34437     {
34438         if (typeof(this.groups[name]) == 'undefined') {
34439             return false;
34440         }
34441         
34442         return this.groups[name] ;
34443     }
34444     
34445 });
34446 /*
34447  * Based on:
34448  * Ext JS Library 1.1.1
34449  * Copyright(c) 2006-2007, Ext JS, LLC.
34450  *
34451  * Originally Released Under LGPL - original licence link has changed is not relivant.
34452  *
34453  * Fork - LGPL
34454  * <script type="text/javascript">
34455  */
34456
34457
34458 /**
34459  * @class Roo.bootstrap.SplitBar
34460  * @extends Roo.util.Observable
34461  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
34462  * <br><br>
34463  * Usage:
34464  * <pre><code>
34465 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
34466                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
34467 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
34468 split.minSize = 100;
34469 split.maxSize = 600;
34470 split.animate = true;
34471 split.on('moved', splitterMoved);
34472 </code></pre>
34473  * @constructor
34474  * Create a new SplitBar
34475  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
34476  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
34477  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34478  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
34479                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
34480                         position of the SplitBar).
34481  */
34482 Roo.bootstrap.SplitBar = function(cfg){
34483     
34484     /** @private */
34485     
34486     //{
34487     //  dragElement : elm
34488     //  resizingElement: el,
34489         // optional..
34490     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
34491     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
34492         // existingProxy ???
34493     //}
34494     
34495     this.el = Roo.get(cfg.dragElement, true);
34496     this.el.dom.unselectable = "on";
34497     /** @private */
34498     this.resizingEl = Roo.get(cfg.resizingElement, true);
34499
34500     /**
34501      * @private
34502      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
34503      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
34504      * @type Number
34505      */
34506     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
34507     
34508     /**
34509      * The minimum size of the resizing element. (Defaults to 0)
34510      * @type Number
34511      */
34512     this.minSize = 0;
34513     
34514     /**
34515      * The maximum size of the resizing element. (Defaults to 2000)
34516      * @type Number
34517      */
34518     this.maxSize = 2000;
34519     
34520     /**
34521      * Whether to animate the transition to the new size
34522      * @type Boolean
34523      */
34524     this.animate = false;
34525     
34526     /**
34527      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
34528      * @type Boolean
34529      */
34530     this.useShim = false;
34531     
34532     /** @private */
34533     this.shim = null;
34534     
34535     if(!cfg.existingProxy){
34536         /** @private */
34537         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
34538     }else{
34539         this.proxy = Roo.get(cfg.existingProxy).dom;
34540     }
34541     /** @private */
34542     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
34543     
34544     /** @private */
34545     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
34546     
34547     /** @private */
34548     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
34549     
34550     /** @private */
34551     this.dragSpecs = {};
34552     
34553     /**
34554      * @private The adapter to use to positon and resize elements
34555      */
34556     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34557     this.adapter.init(this);
34558     
34559     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34560         /** @private */
34561         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
34562         this.el.addClass("roo-splitbar-h");
34563     }else{
34564         /** @private */
34565         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
34566         this.el.addClass("roo-splitbar-v");
34567     }
34568     
34569     this.addEvents({
34570         /**
34571          * @event resize
34572          * Fires when the splitter is moved (alias for {@link #event-moved})
34573          * @param {Roo.bootstrap.SplitBar} this
34574          * @param {Number} newSize the new width or height
34575          */
34576         "resize" : true,
34577         /**
34578          * @event moved
34579          * Fires when the splitter is moved
34580          * @param {Roo.bootstrap.SplitBar} this
34581          * @param {Number} newSize the new width or height
34582          */
34583         "moved" : true,
34584         /**
34585          * @event beforeresize
34586          * Fires before the splitter is dragged
34587          * @param {Roo.bootstrap.SplitBar} this
34588          */
34589         "beforeresize" : true,
34590
34591         "beforeapply" : true
34592     });
34593
34594     Roo.util.Observable.call(this);
34595 };
34596
34597 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
34598     onStartProxyDrag : function(x, y){
34599         this.fireEvent("beforeresize", this);
34600         if(!this.overlay){
34601             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
34602             o.unselectable();
34603             o.enableDisplayMode("block");
34604             // all splitbars share the same overlay
34605             Roo.bootstrap.SplitBar.prototype.overlay = o;
34606         }
34607         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34608         this.overlay.show();
34609         Roo.get(this.proxy).setDisplayed("block");
34610         var size = this.adapter.getElementSize(this);
34611         this.activeMinSize = this.getMinimumSize();;
34612         this.activeMaxSize = this.getMaximumSize();;
34613         var c1 = size - this.activeMinSize;
34614         var c2 = Math.max(this.activeMaxSize - size, 0);
34615         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34616             this.dd.resetConstraints();
34617             this.dd.setXConstraint(
34618                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
34619                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
34620             );
34621             this.dd.setYConstraint(0, 0);
34622         }else{
34623             this.dd.resetConstraints();
34624             this.dd.setXConstraint(0, 0);
34625             this.dd.setYConstraint(
34626                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
34627                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
34628             );
34629          }
34630         this.dragSpecs.startSize = size;
34631         this.dragSpecs.startPoint = [x, y];
34632         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
34633     },
34634     
34635     /** 
34636      * @private Called after the drag operation by the DDProxy
34637      */
34638     onEndProxyDrag : function(e){
34639         Roo.get(this.proxy).setDisplayed(false);
34640         var endPoint = Roo.lib.Event.getXY(e);
34641         if(this.overlay){
34642             this.overlay.hide();
34643         }
34644         var newSize;
34645         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34646             newSize = this.dragSpecs.startSize + 
34647                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
34648                     endPoint[0] - this.dragSpecs.startPoint[0] :
34649                     this.dragSpecs.startPoint[0] - endPoint[0]
34650                 );
34651         }else{
34652             newSize = this.dragSpecs.startSize + 
34653                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
34654                     endPoint[1] - this.dragSpecs.startPoint[1] :
34655                     this.dragSpecs.startPoint[1] - endPoint[1]
34656                 );
34657         }
34658         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
34659         if(newSize != this.dragSpecs.startSize){
34660             if(this.fireEvent('beforeapply', this, newSize) !== false){
34661                 this.adapter.setElementSize(this, newSize);
34662                 this.fireEvent("moved", this, newSize);
34663                 this.fireEvent("resize", this, newSize);
34664             }
34665         }
34666     },
34667     
34668     /**
34669      * Get the adapter this SplitBar uses
34670      * @return The adapter object
34671      */
34672     getAdapter : function(){
34673         return this.adapter;
34674     },
34675     
34676     /**
34677      * Set the adapter this SplitBar uses
34678      * @param {Object} adapter A SplitBar adapter object
34679      */
34680     setAdapter : function(adapter){
34681         this.adapter = adapter;
34682         this.adapter.init(this);
34683     },
34684     
34685     /**
34686      * Gets the minimum size for the resizing element
34687      * @return {Number} The minimum size
34688      */
34689     getMinimumSize : function(){
34690         return this.minSize;
34691     },
34692     
34693     /**
34694      * Sets the minimum size for the resizing element
34695      * @param {Number} minSize The minimum size
34696      */
34697     setMinimumSize : function(minSize){
34698         this.minSize = minSize;
34699     },
34700     
34701     /**
34702      * Gets the maximum size for the resizing element
34703      * @return {Number} The maximum size
34704      */
34705     getMaximumSize : function(){
34706         return this.maxSize;
34707     },
34708     
34709     /**
34710      * Sets the maximum size for the resizing element
34711      * @param {Number} maxSize The maximum size
34712      */
34713     setMaximumSize : function(maxSize){
34714         this.maxSize = maxSize;
34715     },
34716     
34717     /**
34718      * Sets the initialize size for the resizing element
34719      * @param {Number} size The initial size
34720      */
34721     setCurrentSize : function(size){
34722         var oldAnimate = this.animate;
34723         this.animate = false;
34724         this.adapter.setElementSize(this, size);
34725         this.animate = oldAnimate;
34726     },
34727     
34728     /**
34729      * Destroy this splitbar. 
34730      * @param {Boolean} removeEl True to remove the element
34731      */
34732     destroy : function(removeEl){
34733         if(this.shim){
34734             this.shim.remove();
34735         }
34736         this.dd.unreg();
34737         this.proxy.parentNode.removeChild(this.proxy);
34738         if(removeEl){
34739             this.el.remove();
34740         }
34741     }
34742 });
34743
34744 /**
34745  * @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.
34746  */
34747 Roo.bootstrap.SplitBar.createProxy = function(dir){
34748     var proxy = new Roo.Element(document.createElement("div"));
34749     proxy.unselectable();
34750     var cls = 'roo-splitbar-proxy';
34751     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34752     document.body.appendChild(proxy.dom);
34753     return proxy.dom;
34754 };
34755
34756 /** 
34757  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34758  * Default Adapter. It assumes the splitter and resizing element are not positioned
34759  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34760  */
34761 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34762 };
34763
34764 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34765     // do nothing for now
34766     init : function(s){
34767     
34768     },
34769     /**
34770      * Called before drag operations to get the current size of the resizing element. 
34771      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34772      */
34773      getElementSize : function(s){
34774         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34775             return s.resizingEl.getWidth();
34776         }else{
34777             return s.resizingEl.getHeight();
34778         }
34779     },
34780     
34781     /**
34782      * Called after drag operations to set the size of the resizing element.
34783      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34784      * @param {Number} newSize The new size to set
34785      * @param {Function} onComplete A function to be invoked when resizing is complete
34786      */
34787     setElementSize : function(s, newSize, onComplete){
34788         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34789             if(!s.animate){
34790                 s.resizingEl.setWidth(newSize);
34791                 if(onComplete){
34792                     onComplete(s, newSize);
34793                 }
34794             }else{
34795                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34796             }
34797         }else{
34798             
34799             if(!s.animate){
34800                 s.resizingEl.setHeight(newSize);
34801                 if(onComplete){
34802                     onComplete(s, newSize);
34803                 }
34804             }else{
34805                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34806             }
34807         }
34808     }
34809 };
34810
34811 /** 
34812  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34813  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34814  * Adapter that  moves the splitter element to align with the resized sizing element. 
34815  * Used with an absolute positioned SplitBar.
34816  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34817  * document.body, make sure you assign an id to the body element.
34818  */
34819 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34820     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34821     this.container = Roo.get(container);
34822 };
34823
34824 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34825     init : function(s){
34826         this.basic.init(s);
34827     },
34828     
34829     getElementSize : function(s){
34830         return this.basic.getElementSize(s);
34831     },
34832     
34833     setElementSize : function(s, newSize, onComplete){
34834         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34835     },
34836     
34837     moveSplitter : function(s){
34838         var yes = Roo.bootstrap.SplitBar;
34839         switch(s.placement){
34840             case yes.LEFT:
34841                 s.el.setX(s.resizingEl.getRight());
34842                 break;
34843             case yes.RIGHT:
34844                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34845                 break;
34846             case yes.TOP:
34847                 s.el.setY(s.resizingEl.getBottom());
34848                 break;
34849             case yes.BOTTOM:
34850                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34851                 break;
34852         }
34853     }
34854 };
34855
34856 /**
34857  * Orientation constant - Create a vertical SplitBar
34858  * @static
34859  * @type Number
34860  */
34861 Roo.bootstrap.SplitBar.VERTICAL = 1;
34862
34863 /**
34864  * Orientation constant - Create a horizontal SplitBar
34865  * @static
34866  * @type Number
34867  */
34868 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34869
34870 /**
34871  * Placement constant - The resizing element is to the left of the splitter element
34872  * @static
34873  * @type Number
34874  */
34875 Roo.bootstrap.SplitBar.LEFT = 1;
34876
34877 /**
34878  * Placement constant - The resizing element is to the right of the splitter element
34879  * @static
34880  * @type Number
34881  */
34882 Roo.bootstrap.SplitBar.RIGHT = 2;
34883
34884 /**
34885  * Placement constant - The resizing element is positioned above the splitter element
34886  * @static
34887  * @type Number
34888  */
34889 Roo.bootstrap.SplitBar.TOP = 3;
34890
34891 /**
34892  * Placement constant - The resizing element is positioned under splitter element
34893  * @static
34894  * @type Number
34895  */
34896 Roo.bootstrap.SplitBar.BOTTOM = 4;
34897 Roo.namespace("Roo.bootstrap.layout");/*
34898  * Based on:
34899  * Ext JS Library 1.1.1
34900  * Copyright(c) 2006-2007, Ext JS, LLC.
34901  *
34902  * Originally Released Under LGPL - original licence link has changed is not relivant.
34903  *
34904  * Fork - LGPL
34905  * <script type="text/javascript">
34906  */
34907
34908 /**
34909  * @class Roo.bootstrap.layout.Manager
34910  * @extends Roo.bootstrap.Component
34911  * Base class for layout managers.
34912  */
34913 Roo.bootstrap.layout.Manager = function(config)
34914 {
34915     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34916
34917
34918
34919
34920
34921     /** false to disable window resize monitoring @type Boolean */
34922     this.monitorWindowResize = true;
34923     this.regions = {};
34924     this.addEvents({
34925         /**
34926          * @event layout
34927          * Fires when a layout is performed.
34928          * @param {Roo.LayoutManager} this
34929          */
34930         "layout" : true,
34931         /**
34932          * @event regionresized
34933          * Fires when the user resizes a region.
34934          * @param {Roo.LayoutRegion} region The resized region
34935          * @param {Number} newSize The new size (width for east/west, height for north/south)
34936          */
34937         "regionresized" : true,
34938         /**
34939          * @event regioncollapsed
34940          * Fires when a region is collapsed.
34941          * @param {Roo.LayoutRegion} region The collapsed region
34942          */
34943         "regioncollapsed" : true,
34944         /**
34945          * @event regionexpanded
34946          * Fires when a region is expanded.
34947          * @param {Roo.LayoutRegion} region The expanded region
34948          */
34949         "regionexpanded" : true
34950     });
34951     this.updating = false;
34952
34953     if (config.el) {
34954         this.el = Roo.get(config.el);
34955         this.initEvents();
34956     }
34957
34958 };
34959
34960 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34961
34962
34963     regions : null,
34964
34965     monitorWindowResize : true,
34966
34967
34968     updating : false,
34969
34970
34971     onRender : function(ct, position)
34972     {
34973         if(!this.el){
34974             this.el = Roo.get(ct);
34975             this.initEvents();
34976         }
34977         //this.fireEvent('render',this);
34978     },
34979
34980
34981     initEvents: function()
34982     {
34983
34984
34985         // ie scrollbar fix
34986         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34987             document.body.scroll = "no";
34988         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34989             this.el.position('relative');
34990         }
34991         this.id = this.el.id;
34992         this.el.addClass("roo-layout-container");
34993         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34994         if(this.el.dom != document.body ) {
34995             this.el.on('resize', this.layout,this);
34996             this.el.on('show', this.layout,this);
34997         }
34998
34999     },
35000
35001     /**
35002      * Returns true if this layout is currently being updated
35003      * @return {Boolean}
35004      */
35005     isUpdating : function(){
35006         return this.updating;
35007     },
35008
35009     /**
35010      * Suspend the LayoutManager from doing auto-layouts while
35011      * making multiple add or remove calls
35012      */
35013     beginUpdate : function(){
35014         this.updating = true;
35015     },
35016
35017     /**
35018      * Restore auto-layouts and optionally disable the manager from performing a layout
35019      * @param {Boolean} noLayout true to disable a layout update
35020      */
35021     endUpdate : function(noLayout){
35022         this.updating = false;
35023         if(!noLayout){
35024             this.layout();
35025         }
35026     },
35027
35028     layout: function(){
35029         // abstract...
35030     },
35031
35032     onRegionResized : function(region, newSize){
35033         this.fireEvent("regionresized", region, newSize);
35034         this.layout();
35035     },
35036
35037     onRegionCollapsed : function(region){
35038         this.fireEvent("regioncollapsed", region);
35039     },
35040
35041     onRegionExpanded : function(region){
35042         this.fireEvent("regionexpanded", region);
35043     },
35044
35045     /**
35046      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
35047      * performs box-model adjustments.
35048      * @return {Object} The size as an object {width: (the width), height: (the height)}
35049      */
35050     getViewSize : function()
35051     {
35052         var size;
35053         if(this.el.dom != document.body){
35054             size = this.el.getSize();
35055         }else{
35056             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
35057         }
35058         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
35059         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35060         return size;
35061     },
35062
35063     /**
35064      * Returns the Element this layout is bound to.
35065      * @return {Roo.Element}
35066      */
35067     getEl : function(){
35068         return this.el;
35069     },
35070
35071     /**
35072      * Returns the specified region.
35073      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
35074      * @return {Roo.LayoutRegion}
35075      */
35076     getRegion : function(target){
35077         return this.regions[target.toLowerCase()];
35078     },
35079
35080     onWindowResize : function(){
35081         if(this.monitorWindowResize){
35082             this.layout();
35083         }
35084     }
35085 });
35086 /*
35087  * Based on:
35088  * Ext JS Library 1.1.1
35089  * Copyright(c) 2006-2007, Ext JS, LLC.
35090  *
35091  * Originally Released Under LGPL - original licence link has changed is not relivant.
35092  *
35093  * Fork - LGPL
35094  * <script type="text/javascript">
35095  */
35096 /**
35097  * @class Roo.bootstrap.layout.Border
35098  * @extends Roo.bootstrap.layout.Manager
35099  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
35100  * please see: examples/bootstrap/nested.html<br><br>
35101  
35102 <b>The container the layout is rendered into can be either the body element or any other element.
35103 If it is not the body element, the container needs to either be an absolute positioned element,
35104 or you will need to add "position:relative" to the css of the container.  You will also need to specify
35105 the container size if it is not the body element.</b>
35106
35107 * @constructor
35108 * Create a new Border
35109 * @param {Object} config Configuration options
35110  */
35111 Roo.bootstrap.layout.Border = function(config){
35112     config = config || {};
35113     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
35114     
35115     
35116     
35117     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35118         if(config[region]){
35119             config[region].region = region;
35120             this.addRegion(config[region]);
35121         }
35122     },this);
35123     
35124 };
35125
35126 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
35127
35128 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
35129     
35130     parent : false, // this might point to a 'nest' or a ???
35131     
35132     /**
35133      * Creates and adds a new region if it doesn't already exist.
35134      * @param {String} target The target region key (north, south, east, west or center).
35135      * @param {Object} config The regions config object
35136      * @return {BorderLayoutRegion} The new region
35137      */
35138     addRegion : function(config)
35139     {
35140         if(!this.regions[config.region]){
35141             var r = this.factory(config);
35142             this.bindRegion(r);
35143         }
35144         return this.regions[config.region];
35145     },
35146
35147     // private (kinda)
35148     bindRegion : function(r){
35149         this.regions[r.config.region] = r;
35150         
35151         r.on("visibilitychange",    this.layout, this);
35152         r.on("paneladded",          this.layout, this);
35153         r.on("panelremoved",        this.layout, this);
35154         r.on("invalidated",         this.layout, this);
35155         r.on("resized",             this.onRegionResized, this);
35156         r.on("collapsed",           this.onRegionCollapsed, this);
35157         r.on("expanded",            this.onRegionExpanded, this);
35158     },
35159
35160     /**
35161      * Performs a layout update.
35162      */
35163     layout : function()
35164     {
35165         if(this.updating) {
35166             return;
35167         }
35168         
35169         // render all the rebions if they have not been done alreayd?
35170         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
35171             if(this.regions[region] && !this.regions[region].bodyEl){
35172                 this.regions[region].onRender(this.el)
35173             }
35174         },this);
35175         
35176         var size = this.getViewSize();
35177         var w = size.width;
35178         var h = size.height;
35179         var centerW = w;
35180         var centerH = h;
35181         var centerY = 0;
35182         var centerX = 0;
35183         //var x = 0, y = 0;
35184
35185         var rs = this.regions;
35186         var north = rs["north"];
35187         var south = rs["south"]; 
35188         var west = rs["west"];
35189         var east = rs["east"];
35190         var center = rs["center"];
35191         //if(this.hideOnLayout){ // not supported anymore
35192             //c.el.setStyle("display", "none");
35193         //}
35194         if(north && north.isVisible()){
35195             var b = north.getBox();
35196             var m = north.getMargins();
35197             b.width = w - (m.left+m.right);
35198             b.x = m.left;
35199             b.y = m.top;
35200             centerY = b.height + b.y + m.bottom;
35201             centerH -= centerY;
35202             north.updateBox(this.safeBox(b));
35203         }
35204         if(south && south.isVisible()){
35205             var b = south.getBox();
35206             var m = south.getMargins();
35207             b.width = w - (m.left+m.right);
35208             b.x = m.left;
35209             var totalHeight = (b.height + m.top + m.bottom);
35210             b.y = h - totalHeight + m.top;
35211             centerH -= totalHeight;
35212             south.updateBox(this.safeBox(b));
35213         }
35214         if(west && west.isVisible()){
35215             var b = west.getBox();
35216             var m = west.getMargins();
35217             b.height = centerH - (m.top+m.bottom);
35218             b.x = m.left;
35219             b.y = centerY + m.top;
35220             var totalWidth = (b.width + m.left + m.right);
35221             centerX += totalWidth;
35222             centerW -= totalWidth;
35223             west.updateBox(this.safeBox(b));
35224         }
35225         if(east && east.isVisible()){
35226             var b = east.getBox();
35227             var m = east.getMargins();
35228             b.height = centerH - (m.top+m.bottom);
35229             var totalWidth = (b.width + m.left + m.right);
35230             b.x = w - totalWidth + m.left;
35231             b.y = centerY + m.top;
35232             centerW -= totalWidth;
35233             east.updateBox(this.safeBox(b));
35234         }
35235         if(center){
35236             var m = center.getMargins();
35237             var centerBox = {
35238                 x: centerX + m.left,
35239                 y: centerY + m.top,
35240                 width: centerW - (m.left+m.right),
35241                 height: centerH - (m.top+m.bottom)
35242             };
35243             //if(this.hideOnLayout){
35244                 //center.el.setStyle("display", "block");
35245             //}
35246             center.updateBox(this.safeBox(centerBox));
35247         }
35248         this.el.repaint();
35249         this.fireEvent("layout", this);
35250     },
35251
35252     // private
35253     safeBox : function(box){
35254         box.width = Math.max(0, box.width);
35255         box.height = Math.max(0, box.height);
35256         return box;
35257     },
35258
35259     /**
35260      * Adds a ContentPanel (or subclass) to this layout.
35261      * @param {String} target The target region key (north, south, east, west or center).
35262      * @param {Roo.ContentPanel} panel The panel to add
35263      * @return {Roo.ContentPanel} The added panel
35264      */
35265     add : function(target, panel){
35266          
35267         target = target.toLowerCase();
35268         return this.regions[target].add(panel);
35269     },
35270
35271     /**
35272      * Remove a ContentPanel (or subclass) to this layout.
35273      * @param {String} target The target region key (north, south, east, west or center).
35274      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
35275      * @return {Roo.ContentPanel} The removed panel
35276      */
35277     remove : function(target, panel){
35278         target = target.toLowerCase();
35279         return this.regions[target].remove(panel);
35280     },
35281
35282     /**
35283      * Searches all regions for a panel with the specified id
35284      * @param {String} panelId
35285      * @return {Roo.ContentPanel} The panel or null if it wasn't found
35286      */
35287     findPanel : function(panelId){
35288         var rs = this.regions;
35289         for(var target in rs){
35290             if(typeof rs[target] != "function"){
35291                 var p = rs[target].getPanel(panelId);
35292                 if(p){
35293                     return p;
35294                 }
35295             }
35296         }
35297         return null;
35298     },
35299
35300     /**
35301      * Searches all regions for a panel with the specified id and activates (shows) it.
35302      * @param {String/ContentPanel} panelId The panels id or the panel itself
35303      * @return {Roo.ContentPanel} The shown panel or null
35304      */
35305     showPanel : function(panelId) {
35306       var rs = this.regions;
35307       for(var target in rs){
35308          var r = rs[target];
35309          if(typeof r != "function"){
35310             if(r.hasPanel(panelId)){
35311                return r.showPanel(panelId);
35312             }
35313          }
35314       }
35315       return null;
35316    },
35317
35318    /**
35319      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
35320      * @param {Roo.state.Provider} provider (optional) An alternate state provider
35321      */
35322    /*
35323     restoreState : function(provider){
35324         if(!provider){
35325             provider = Roo.state.Manager;
35326         }
35327         var sm = new Roo.LayoutStateManager();
35328         sm.init(this, provider);
35329     },
35330 */
35331  
35332  
35333     /**
35334      * Adds a xtype elements to the layout.
35335      * <pre><code>
35336
35337 layout.addxtype({
35338        xtype : 'ContentPanel',
35339        region: 'west',
35340        items: [ .... ]
35341    }
35342 );
35343
35344 layout.addxtype({
35345         xtype : 'NestedLayoutPanel',
35346         region: 'west',
35347         layout: {
35348            center: { },
35349            west: { }   
35350         },
35351         items : [ ... list of content panels or nested layout panels.. ]
35352    }
35353 );
35354 </code></pre>
35355      * @param {Object} cfg Xtype definition of item to add.
35356      */
35357     addxtype : function(cfg)
35358     {
35359         // basically accepts a pannel...
35360         // can accept a layout region..!?!?
35361         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
35362         
35363         
35364         // theory?  children can only be panels??
35365         
35366         //if (!cfg.xtype.match(/Panel$/)) {
35367         //    return false;
35368         //}
35369         var ret = false;
35370         
35371         if (typeof(cfg.region) == 'undefined') {
35372             Roo.log("Failed to add Panel, region was not set");
35373             Roo.log(cfg);
35374             return false;
35375         }
35376         var region = cfg.region;
35377         delete cfg.region;
35378         
35379           
35380         var xitems = [];
35381         if (cfg.items) {
35382             xitems = cfg.items;
35383             delete cfg.items;
35384         }
35385         var nb = false;
35386         
35387         if ( region == 'center') {
35388             Roo.log("Center: " + cfg.title);
35389         }
35390         
35391         
35392         switch(cfg.xtype) 
35393         {
35394             case 'Content':  // ContentPanel (el, cfg)
35395             case 'Scroll':  // ContentPanel (el, cfg)
35396             case 'View': 
35397                 cfg.autoCreate = cfg.autoCreate || true;
35398                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35399                 //} else {
35400                 //    var el = this.el.createChild();
35401                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
35402                 //}
35403                 
35404                 this.add(region, ret);
35405                 break;
35406             
35407             /*
35408             case 'TreePanel': // our new panel!
35409                 cfg.el = this.el.createChild();
35410                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35411                 this.add(region, ret);
35412                 break;
35413             */
35414             
35415             case 'Nest': 
35416                 // create a new Layout (which is  a Border Layout...
35417                 
35418                 var clayout = cfg.layout;
35419                 clayout.el  = this.el.createChild();
35420                 clayout.items   = clayout.items  || [];
35421                 
35422                 delete cfg.layout;
35423                 
35424                 // replace this exitems with the clayout ones..
35425                 xitems = clayout.items;
35426                  
35427                 // force background off if it's in center...
35428                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
35429                     cfg.background = false;
35430                 }
35431                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
35432                 
35433                 
35434                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35435                 //console.log('adding nested layout panel '  + cfg.toSource());
35436                 this.add(region, ret);
35437                 nb = {}; /// find first...
35438                 break;
35439             
35440             case 'Grid':
35441                 
35442                 // needs grid and region
35443                 
35444                 //var el = this.getRegion(region).el.createChild();
35445                 /*
35446                  *var el = this.el.createChild();
35447                 // create the grid first...
35448                 cfg.grid.container = el;
35449                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
35450                 */
35451                 
35452                 if (region == 'center' && this.active ) {
35453                     cfg.background = false;
35454                 }
35455                 
35456                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
35457                 
35458                 this.add(region, ret);
35459                 /*
35460                 if (cfg.background) {
35461                     // render grid on panel activation (if panel background)
35462                     ret.on('activate', function(gp) {
35463                         if (!gp.grid.rendered) {
35464                     //        gp.grid.render(el);
35465                         }
35466                     });
35467                 } else {
35468                   //  cfg.grid.render(el);
35469                 }
35470                 */
35471                 break;
35472            
35473            
35474             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
35475                 // it was the old xcomponent building that caused this before.
35476                 // espeically if border is the top element in the tree.
35477                 ret = this;
35478                 break; 
35479                 
35480                     
35481                 
35482                 
35483                 
35484             default:
35485                 /*
35486                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
35487                     
35488                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
35489                     this.add(region, ret);
35490                 } else {
35491                 */
35492                     Roo.log(cfg);
35493                     throw "Can not add '" + cfg.xtype + "' to Border";
35494                     return null;
35495              
35496                                 
35497              
35498         }
35499         this.beginUpdate();
35500         // add children..
35501         var region = '';
35502         var abn = {};
35503         Roo.each(xitems, function(i)  {
35504             region = nb && i.region ? i.region : false;
35505             
35506             var add = ret.addxtype(i);
35507            
35508             if (region) {
35509                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
35510                 if (!i.background) {
35511                     abn[region] = nb[region] ;
35512                 }
35513             }
35514             
35515         });
35516         this.endUpdate();
35517
35518         // make the last non-background panel active..
35519         //if (nb) { Roo.log(abn); }
35520         if (nb) {
35521             
35522             for(var r in abn) {
35523                 region = this.getRegion(r);
35524                 if (region) {
35525                     // tried using nb[r], but it does not work..
35526                      
35527                     region.showPanel(abn[r]);
35528                    
35529                 }
35530             }
35531         }
35532         return ret;
35533         
35534     },
35535     
35536     
35537 // private
35538     factory : function(cfg)
35539     {
35540         
35541         var validRegions = Roo.bootstrap.layout.Border.regions;
35542
35543         var target = cfg.region;
35544         cfg.mgr = this;
35545         
35546         var r = Roo.bootstrap.layout;
35547         Roo.log(target);
35548         switch(target){
35549             case "north":
35550                 return new r.North(cfg);
35551             case "south":
35552                 return new r.South(cfg);
35553             case "east":
35554                 return new r.East(cfg);
35555             case "west":
35556                 return new r.West(cfg);
35557             case "center":
35558                 return new r.Center(cfg);
35559         }
35560         throw 'Layout region "'+target+'" not supported.';
35561     }
35562     
35563     
35564 });
35565  /*
35566  * Based on:
35567  * Ext JS Library 1.1.1
35568  * Copyright(c) 2006-2007, Ext JS, LLC.
35569  *
35570  * Originally Released Under LGPL - original licence link has changed is not relivant.
35571  *
35572  * Fork - LGPL
35573  * <script type="text/javascript">
35574  */
35575  
35576 /**
35577  * @class Roo.bootstrap.layout.Basic
35578  * @extends Roo.util.Observable
35579  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
35580  * and does not have a titlebar, tabs or any other features. All it does is size and position 
35581  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
35582  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35583  * @cfg {string}   region  the region that it inhabits..
35584  * @cfg {bool}   skipConfig skip config?
35585  * 
35586
35587  */
35588 Roo.bootstrap.layout.Basic = function(config){
35589     
35590     this.mgr = config.mgr;
35591     
35592     this.position = config.region;
35593     
35594     var skipConfig = config.skipConfig;
35595     
35596     this.events = {
35597         /**
35598          * @scope Roo.BasicLayoutRegion
35599          */
35600         
35601         /**
35602          * @event beforeremove
35603          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
35604          * @param {Roo.LayoutRegion} this
35605          * @param {Roo.ContentPanel} panel The panel
35606          * @param {Object} e The cancel event object
35607          */
35608         "beforeremove" : true,
35609         /**
35610          * @event invalidated
35611          * Fires when the layout for this region is changed.
35612          * @param {Roo.LayoutRegion} this
35613          */
35614         "invalidated" : true,
35615         /**
35616          * @event visibilitychange
35617          * Fires when this region is shown or hidden 
35618          * @param {Roo.LayoutRegion} this
35619          * @param {Boolean} visibility true or false
35620          */
35621         "visibilitychange" : true,
35622         /**
35623          * @event paneladded
35624          * Fires when a panel is added. 
35625          * @param {Roo.LayoutRegion} this
35626          * @param {Roo.ContentPanel} panel The panel
35627          */
35628         "paneladded" : true,
35629         /**
35630          * @event panelremoved
35631          * Fires when a panel is removed. 
35632          * @param {Roo.LayoutRegion} this
35633          * @param {Roo.ContentPanel} panel The panel
35634          */
35635         "panelremoved" : true,
35636         /**
35637          * @event beforecollapse
35638          * Fires when this region before collapse.
35639          * @param {Roo.LayoutRegion} this
35640          */
35641         "beforecollapse" : true,
35642         /**
35643          * @event collapsed
35644          * Fires when this region is collapsed.
35645          * @param {Roo.LayoutRegion} this
35646          */
35647         "collapsed" : true,
35648         /**
35649          * @event expanded
35650          * Fires when this region is expanded.
35651          * @param {Roo.LayoutRegion} this
35652          */
35653         "expanded" : true,
35654         /**
35655          * @event slideshow
35656          * Fires when this region is slid into view.
35657          * @param {Roo.LayoutRegion} this
35658          */
35659         "slideshow" : true,
35660         /**
35661          * @event slidehide
35662          * Fires when this region slides out of view. 
35663          * @param {Roo.LayoutRegion} this
35664          */
35665         "slidehide" : true,
35666         /**
35667          * @event panelactivated
35668          * Fires when a panel is activated. 
35669          * @param {Roo.LayoutRegion} this
35670          * @param {Roo.ContentPanel} panel The activated panel
35671          */
35672         "panelactivated" : true,
35673         /**
35674          * @event resized
35675          * Fires when the user resizes this region. 
35676          * @param {Roo.LayoutRegion} this
35677          * @param {Number} newSize The new size (width for east/west, height for north/south)
35678          */
35679         "resized" : true
35680     };
35681     /** A collection of panels in this region. @type Roo.util.MixedCollection */
35682     this.panels = new Roo.util.MixedCollection();
35683     this.panels.getKey = this.getPanelId.createDelegate(this);
35684     this.box = null;
35685     this.activePanel = null;
35686     // ensure listeners are added...
35687     
35688     if (config.listeners || config.events) {
35689         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
35690             listeners : config.listeners || {},
35691             events : config.events || {}
35692         });
35693     }
35694     
35695     if(skipConfig !== true){
35696         this.applyConfig(config);
35697     }
35698 };
35699
35700 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
35701 {
35702     getPanelId : function(p){
35703         return p.getId();
35704     },
35705     
35706     applyConfig : function(config){
35707         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35708         this.config = config;
35709         
35710     },
35711     
35712     /**
35713      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35714      * the width, for horizontal (north, south) the height.
35715      * @param {Number} newSize The new width or height
35716      */
35717     resizeTo : function(newSize){
35718         var el = this.el ? this.el :
35719                  (this.activePanel ? this.activePanel.getEl() : null);
35720         if(el){
35721             switch(this.position){
35722                 case "east":
35723                 case "west":
35724                     el.setWidth(newSize);
35725                     this.fireEvent("resized", this, newSize);
35726                 break;
35727                 case "north":
35728                 case "south":
35729                     el.setHeight(newSize);
35730                     this.fireEvent("resized", this, newSize);
35731                 break;                
35732             }
35733         }
35734     },
35735     
35736     getBox : function(){
35737         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35738     },
35739     
35740     getMargins : function(){
35741         return this.margins;
35742     },
35743     
35744     updateBox : function(box){
35745         this.box = box;
35746         var el = this.activePanel.getEl();
35747         el.dom.style.left = box.x + "px";
35748         el.dom.style.top = box.y + "px";
35749         this.activePanel.setSize(box.width, box.height);
35750     },
35751     
35752     /**
35753      * Returns the container element for this region.
35754      * @return {Roo.Element}
35755      */
35756     getEl : function(){
35757         return this.activePanel;
35758     },
35759     
35760     /**
35761      * Returns true if this region is currently visible.
35762      * @return {Boolean}
35763      */
35764     isVisible : function(){
35765         return this.activePanel ? true : false;
35766     },
35767     
35768     setActivePanel : function(panel){
35769         panel = this.getPanel(panel);
35770         if(this.activePanel && this.activePanel != panel){
35771             this.activePanel.setActiveState(false);
35772             this.activePanel.getEl().setLeftTop(-10000,-10000);
35773         }
35774         this.activePanel = panel;
35775         panel.setActiveState(true);
35776         if(this.box){
35777             panel.setSize(this.box.width, this.box.height);
35778         }
35779         this.fireEvent("panelactivated", this, panel);
35780         this.fireEvent("invalidated");
35781     },
35782     
35783     /**
35784      * Show the specified panel.
35785      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35786      * @return {Roo.ContentPanel} The shown panel or null
35787      */
35788     showPanel : function(panel){
35789         panel = this.getPanel(panel);
35790         if(panel){
35791             this.setActivePanel(panel);
35792         }
35793         return panel;
35794     },
35795     
35796     /**
35797      * Get the active panel for this region.
35798      * @return {Roo.ContentPanel} The active panel or null
35799      */
35800     getActivePanel : function(){
35801         return this.activePanel;
35802     },
35803     
35804     /**
35805      * Add the passed ContentPanel(s)
35806      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35807      * @return {Roo.ContentPanel} The panel added (if only one was added)
35808      */
35809     add : function(panel){
35810         if(arguments.length > 1){
35811             for(var i = 0, len = arguments.length; i < len; i++) {
35812                 this.add(arguments[i]);
35813             }
35814             return null;
35815         }
35816         if(this.hasPanel(panel)){
35817             this.showPanel(panel);
35818             return panel;
35819         }
35820         var el = panel.getEl();
35821         if(el.dom.parentNode != this.mgr.el.dom){
35822             this.mgr.el.dom.appendChild(el.dom);
35823         }
35824         if(panel.setRegion){
35825             panel.setRegion(this);
35826         }
35827         this.panels.add(panel);
35828         el.setStyle("position", "absolute");
35829         if(!panel.background){
35830             this.setActivePanel(panel);
35831             if(this.config.initialSize && this.panels.getCount()==1){
35832                 this.resizeTo(this.config.initialSize);
35833             }
35834         }
35835         this.fireEvent("paneladded", this, panel);
35836         return panel;
35837     },
35838     
35839     /**
35840      * Returns true if the panel is in this region.
35841      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35842      * @return {Boolean}
35843      */
35844     hasPanel : function(panel){
35845         if(typeof panel == "object"){ // must be panel obj
35846             panel = panel.getId();
35847         }
35848         return this.getPanel(panel) ? true : false;
35849     },
35850     
35851     /**
35852      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35853      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35854      * @param {Boolean} preservePanel Overrides the config preservePanel option
35855      * @return {Roo.ContentPanel} The panel that was removed
35856      */
35857     remove : function(panel, preservePanel){
35858         panel = this.getPanel(panel);
35859         if(!panel){
35860             return null;
35861         }
35862         var e = {};
35863         this.fireEvent("beforeremove", this, panel, e);
35864         if(e.cancel === true){
35865             return null;
35866         }
35867         var panelId = panel.getId();
35868         this.panels.removeKey(panelId);
35869         return panel;
35870     },
35871     
35872     /**
35873      * Returns the panel specified or null if it's not in this region.
35874      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35875      * @return {Roo.ContentPanel}
35876      */
35877     getPanel : function(id){
35878         if(typeof id == "object"){ // must be panel obj
35879             return id;
35880         }
35881         return this.panels.get(id);
35882     },
35883     
35884     /**
35885      * Returns this regions position (north/south/east/west/center).
35886      * @return {String} 
35887      */
35888     getPosition: function(){
35889         return this.position;    
35890     }
35891 });/*
35892  * Based on:
35893  * Ext JS Library 1.1.1
35894  * Copyright(c) 2006-2007, Ext JS, LLC.
35895  *
35896  * Originally Released Under LGPL - original licence link has changed is not relivant.
35897  *
35898  * Fork - LGPL
35899  * <script type="text/javascript">
35900  */
35901  
35902 /**
35903  * @class Roo.bootstrap.layout.Region
35904  * @extends Roo.bootstrap.layout.Basic
35905  * This class represents a region in a layout manager.
35906  
35907  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35908  * @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})
35909  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35910  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35911  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35912  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35913  * @cfg {String}    title           The title for the region (overrides panel titles)
35914  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35915  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35916  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35917  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35918  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35919  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35920  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35921  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35922  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35923  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35924
35925  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35926  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35927  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35928  * @cfg {Number}    width           For East/West panels
35929  * @cfg {Number}    height          For North/South panels
35930  * @cfg {Boolean}   split           To show the splitter
35931  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35932  * 
35933  * @cfg {string}   cls             Extra CSS classes to add to region
35934  * 
35935  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35936  * @cfg {string}   region  the region that it inhabits..
35937  *
35938
35939  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35940  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35941
35942  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35943  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35944  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35945  */
35946 Roo.bootstrap.layout.Region = function(config)
35947 {
35948     this.applyConfig(config);
35949
35950     var mgr = config.mgr;
35951     var pos = config.region;
35952     config.skipConfig = true;
35953     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35954     
35955     if (mgr.el) {
35956         this.onRender(mgr.el);   
35957     }
35958      
35959     this.visible = true;
35960     this.collapsed = false;
35961     this.unrendered_panels = [];
35962 };
35963
35964 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35965
35966     position: '', // set by wrapper (eg. north/south etc..)
35967     unrendered_panels : null,  // unrendered panels.
35968     
35969     tabPosition : false,
35970     
35971     mgr: false, // points to 'Border'
35972     
35973     
35974     createBody : function(){
35975         /** This region's body element 
35976         * @type Roo.Element */
35977         this.bodyEl = this.el.createChild({
35978                 tag: "div",
35979                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35980         });
35981     },
35982
35983     onRender: function(ctr, pos)
35984     {
35985         var dh = Roo.DomHelper;
35986         /** This region's container element 
35987         * @type Roo.Element */
35988         this.el = dh.append(ctr.dom, {
35989                 tag: "div",
35990                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35991             }, true);
35992         /** This region's title element 
35993         * @type Roo.Element */
35994     
35995         this.titleEl = dh.append(this.el.dom,  {
35996                 tag: "div",
35997                 unselectable: "on",
35998                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35999                 children:[
36000                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
36001                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
36002                 ]
36003             }, true);
36004         
36005         this.titleEl.enableDisplayMode();
36006         /** This region's title text element 
36007         * @type HTMLElement */
36008         this.titleTextEl = this.titleEl.dom.firstChild;
36009         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
36010         /*
36011         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
36012         this.closeBtn.enableDisplayMode();
36013         this.closeBtn.on("click", this.closeClicked, this);
36014         this.closeBtn.hide();
36015     */
36016         this.createBody(this.config);
36017         if(this.config.hideWhenEmpty){
36018             this.hide();
36019             this.on("paneladded", this.validateVisibility, this);
36020             this.on("panelremoved", this.validateVisibility, this);
36021         }
36022         if(this.autoScroll){
36023             this.bodyEl.setStyle("overflow", "auto");
36024         }else{
36025             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
36026         }
36027         //if(c.titlebar !== false){
36028             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
36029                 this.titleEl.hide();
36030             }else{
36031                 this.titleEl.show();
36032                 if(this.config.title){
36033                     this.titleTextEl.innerHTML = this.config.title;
36034                 }
36035             }
36036         //}
36037         if(this.config.collapsed){
36038             this.collapse(true);
36039         }
36040         if(this.config.hidden){
36041             this.hide();
36042         }
36043         
36044         if (this.unrendered_panels && this.unrendered_panels.length) {
36045             for (var i =0;i< this.unrendered_panels.length; i++) {
36046                 this.add(this.unrendered_panels[i]);
36047             }
36048             this.unrendered_panels = null;
36049             
36050         }
36051         
36052     },
36053     
36054     applyConfig : function(c)
36055     {
36056         /*
36057          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
36058             var dh = Roo.DomHelper;
36059             if(c.titlebar !== false){
36060                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
36061                 this.collapseBtn.on("click", this.collapse, this);
36062                 this.collapseBtn.enableDisplayMode();
36063                 /*
36064                 if(c.showPin === true || this.showPin){
36065                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
36066                     this.stickBtn.enableDisplayMode();
36067                     this.stickBtn.on("click", this.expand, this);
36068                     this.stickBtn.hide();
36069                 }
36070                 
36071             }
36072             */
36073             /** This region's collapsed element
36074             * @type Roo.Element */
36075             /*
36076              *
36077             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
36078                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
36079             ]}, true);
36080             
36081             if(c.floatable !== false){
36082                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
36083                this.collapsedEl.on("click", this.collapseClick, this);
36084             }
36085
36086             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
36087                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
36088                    id: "message", unselectable: "on", style:{"float":"left"}});
36089                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
36090              }
36091             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
36092             this.expandBtn.on("click", this.expand, this);
36093             
36094         }
36095         
36096         if(this.collapseBtn){
36097             this.collapseBtn.setVisible(c.collapsible == true);
36098         }
36099         
36100         this.cmargins = c.cmargins || this.cmargins ||
36101                          (this.position == "west" || this.position == "east" ?
36102                              {top: 0, left: 2, right:2, bottom: 0} :
36103                              {top: 2, left: 0, right:0, bottom: 2});
36104         */
36105         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
36106         
36107         
36108         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
36109         
36110         this.autoScroll = c.autoScroll || false;
36111         
36112         
36113        
36114         
36115         this.duration = c.duration || .30;
36116         this.slideDuration = c.slideDuration || .45;
36117         this.config = c;
36118        
36119     },
36120     /**
36121      * Returns true if this region is currently visible.
36122      * @return {Boolean}
36123      */
36124     isVisible : function(){
36125         return this.visible;
36126     },
36127
36128     /**
36129      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
36130      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
36131      */
36132     //setCollapsedTitle : function(title){
36133     //    title = title || "&#160;";
36134      //   if(this.collapsedTitleTextEl){
36135       //      this.collapsedTitleTextEl.innerHTML = title;
36136        // }
36137     //},
36138
36139     getBox : function(){
36140         var b;
36141       //  if(!this.collapsed){
36142             b = this.el.getBox(false, true);
36143        // }else{
36144           //  b = this.collapsedEl.getBox(false, true);
36145         //}
36146         return b;
36147     },
36148
36149     getMargins : function(){
36150         return this.margins;
36151         //return this.collapsed ? this.cmargins : this.margins;
36152     },
36153 /*
36154     highlight : function(){
36155         this.el.addClass("x-layout-panel-dragover");
36156     },
36157
36158     unhighlight : function(){
36159         this.el.removeClass("x-layout-panel-dragover");
36160     },
36161 */
36162     updateBox : function(box)
36163     {
36164         if (!this.bodyEl) {
36165             return; // not rendered yet..
36166         }
36167         
36168         this.box = box;
36169         if(!this.collapsed){
36170             this.el.dom.style.left = box.x + "px";
36171             this.el.dom.style.top = box.y + "px";
36172             this.updateBody(box.width, box.height);
36173         }else{
36174             this.collapsedEl.dom.style.left = box.x + "px";
36175             this.collapsedEl.dom.style.top = box.y + "px";
36176             this.collapsedEl.setSize(box.width, box.height);
36177         }
36178         if(this.tabs){
36179             this.tabs.autoSizeTabs();
36180         }
36181     },
36182
36183     updateBody : function(w, h)
36184     {
36185         if(w !== null){
36186             this.el.setWidth(w);
36187             w -= this.el.getBorderWidth("rl");
36188             if(this.config.adjustments){
36189                 w += this.config.adjustments[0];
36190             }
36191         }
36192         if(h !== null && h > 0){
36193             this.el.setHeight(h);
36194             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
36195             h -= this.el.getBorderWidth("tb");
36196             if(this.config.adjustments){
36197                 h += this.config.adjustments[1];
36198             }
36199             this.bodyEl.setHeight(h);
36200             if(this.tabs){
36201                 h = this.tabs.syncHeight(h);
36202             }
36203         }
36204         if(this.panelSize){
36205             w = w !== null ? w : this.panelSize.width;
36206             h = h !== null ? h : this.panelSize.height;
36207         }
36208         if(this.activePanel){
36209             var el = this.activePanel.getEl();
36210             w = w !== null ? w : el.getWidth();
36211             h = h !== null ? h : el.getHeight();
36212             this.panelSize = {width: w, height: h};
36213             this.activePanel.setSize(w, h);
36214         }
36215         if(Roo.isIE && this.tabs){
36216             this.tabs.el.repaint();
36217         }
36218     },
36219
36220     /**
36221      * Returns the container element for this region.
36222      * @return {Roo.Element}
36223      */
36224     getEl : function(){
36225         return this.el;
36226     },
36227
36228     /**
36229      * Hides this region.
36230      */
36231     hide : function(){
36232         //if(!this.collapsed){
36233             this.el.dom.style.left = "-2000px";
36234             this.el.hide();
36235         //}else{
36236          //   this.collapsedEl.dom.style.left = "-2000px";
36237          //   this.collapsedEl.hide();
36238        // }
36239         this.visible = false;
36240         this.fireEvent("visibilitychange", this, false);
36241     },
36242
36243     /**
36244      * Shows this region if it was previously hidden.
36245      */
36246     show : function(){
36247         //if(!this.collapsed){
36248             this.el.show();
36249         //}else{
36250         //    this.collapsedEl.show();
36251        // }
36252         this.visible = true;
36253         this.fireEvent("visibilitychange", this, true);
36254     },
36255 /*
36256     closeClicked : function(){
36257         if(this.activePanel){
36258             this.remove(this.activePanel);
36259         }
36260     },
36261
36262     collapseClick : function(e){
36263         if(this.isSlid){
36264            e.stopPropagation();
36265            this.slideIn();
36266         }else{
36267            e.stopPropagation();
36268            this.slideOut();
36269         }
36270     },
36271 */
36272     /**
36273      * Collapses this region.
36274      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
36275      */
36276     /*
36277     collapse : function(skipAnim, skipCheck = false){
36278         if(this.collapsed) {
36279             return;
36280         }
36281         
36282         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
36283             
36284             this.collapsed = true;
36285             if(this.split){
36286                 this.split.el.hide();
36287             }
36288             if(this.config.animate && skipAnim !== true){
36289                 this.fireEvent("invalidated", this);
36290                 this.animateCollapse();
36291             }else{
36292                 this.el.setLocation(-20000,-20000);
36293                 this.el.hide();
36294                 this.collapsedEl.show();
36295                 this.fireEvent("collapsed", this);
36296                 this.fireEvent("invalidated", this);
36297             }
36298         }
36299         
36300     },
36301 */
36302     animateCollapse : function(){
36303         // overridden
36304     },
36305
36306     /**
36307      * Expands this region if it was previously collapsed.
36308      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
36309      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
36310      */
36311     /*
36312     expand : function(e, skipAnim){
36313         if(e) {
36314             e.stopPropagation();
36315         }
36316         if(!this.collapsed || this.el.hasActiveFx()) {
36317             return;
36318         }
36319         if(this.isSlid){
36320             this.afterSlideIn();
36321             skipAnim = true;
36322         }
36323         this.collapsed = false;
36324         if(this.config.animate && skipAnim !== true){
36325             this.animateExpand();
36326         }else{
36327             this.el.show();
36328             if(this.split){
36329                 this.split.el.show();
36330             }
36331             this.collapsedEl.setLocation(-2000,-2000);
36332             this.collapsedEl.hide();
36333             this.fireEvent("invalidated", this);
36334             this.fireEvent("expanded", this);
36335         }
36336     },
36337 */
36338     animateExpand : function(){
36339         // overridden
36340     },
36341
36342     initTabs : function()
36343     {
36344         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
36345         
36346         var ts = new Roo.bootstrap.panel.Tabs({
36347             el: this.bodyEl.dom,
36348             region : this,
36349             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
36350             disableTooltips: this.config.disableTabTips,
36351             toolbar : this.config.toolbar
36352         });
36353         
36354         if(this.config.hideTabs){
36355             ts.stripWrap.setDisplayed(false);
36356         }
36357         this.tabs = ts;
36358         ts.resizeTabs = this.config.resizeTabs === true;
36359         ts.minTabWidth = this.config.minTabWidth || 40;
36360         ts.maxTabWidth = this.config.maxTabWidth || 250;
36361         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
36362         ts.monitorResize = false;
36363         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
36364         ts.bodyEl.addClass('roo-layout-tabs-body');
36365         this.panels.each(this.initPanelAsTab, this);
36366     },
36367
36368     initPanelAsTab : function(panel){
36369         var ti = this.tabs.addTab(
36370             panel.getEl().id,
36371             panel.getTitle(),
36372             null,
36373             this.config.closeOnTab && panel.isClosable(),
36374             panel.tpl
36375         );
36376         if(panel.tabTip !== undefined){
36377             ti.setTooltip(panel.tabTip);
36378         }
36379         ti.on("activate", function(){
36380               this.setActivePanel(panel);
36381         }, this);
36382         
36383         if(this.config.closeOnTab){
36384             ti.on("beforeclose", function(t, e){
36385                 e.cancel = true;
36386                 this.remove(panel);
36387             }, this);
36388         }
36389         
36390         panel.tabItem = ti;
36391         
36392         return ti;
36393     },
36394
36395     updatePanelTitle : function(panel, title)
36396     {
36397         if(this.activePanel == panel){
36398             this.updateTitle(title);
36399         }
36400         if(this.tabs){
36401             var ti = this.tabs.getTab(panel.getEl().id);
36402             ti.setText(title);
36403             if(panel.tabTip !== undefined){
36404                 ti.setTooltip(panel.tabTip);
36405             }
36406         }
36407     },
36408
36409     updateTitle : function(title){
36410         if(this.titleTextEl && !this.config.title){
36411             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
36412         }
36413     },
36414
36415     setActivePanel : function(panel)
36416     {
36417         panel = this.getPanel(panel);
36418         if(this.activePanel && this.activePanel != panel){
36419             if(this.activePanel.setActiveState(false) === false){
36420                 return;
36421             }
36422         }
36423         this.activePanel = panel;
36424         panel.setActiveState(true);
36425         if(this.panelSize){
36426             panel.setSize(this.panelSize.width, this.panelSize.height);
36427         }
36428         if(this.closeBtn){
36429             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
36430         }
36431         this.updateTitle(panel.getTitle());
36432         if(this.tabs){
36433             this.fireEvent("invalidated", this);
36434         }
36435         this.fireEvent("panelactivated", this, panel);
36436     },
36437
36438     /**
36439      * Shows the specified panel.
36440      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
36441      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
36442      */
36443     showPanel : function(panel)
36444     {
36445         panel = this.getPanel(panel);
36446         if(panel){
36447             if(this.tabs){
36448                 var tab = this.tabs.getTab(panel.getEl().id);
36449                 if(tab.isHidden()){
36450                     this.tabs.unhideTab(tab.id);
36451                 }
36452                 tab.activate();
36453             }else{
36454                 this.setActivePanel(panel);
36455             }
36456         }
36457         return panel;
36458     },
36459
36460     /**
36461      * Get the active panel for this region.
36462      * @return {Roo.ContentPanel} The active panel or null
36463      */
36464     getActivePanel : function(){
36465         return this.activePanel;
36466     },
36467
36468     validateVisibility : function(){
36469         if(this.panels.getCount() < 1){
36470             this.updateTitle("&#160;");
36471             this.closeBtn.hide();
36472             this.hide();
36473         }else{
36474             if(!this.isVisible()){
36475                 this.show();
36476             }
36477         }
36478     },
36479
36480     /**
36481      * Adds the passed ContentPanel(s) to this region.
36482      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
36483      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
36484      */
36485     add : function(panel)
36486     {
36487         if(arguments.length > 1){
36488             for(var i = 0, len = arguments.length; i < len; i++) {
36489                 this.add(arguments[i]);
36490             }
36491             return null;
36492         }
36493         
36494         // if we have not been rendered yet, then we can not really do much of this..
36495         if (!this.bodyEl) {
36496             this.unrendered_panels.push(panel);
36497             return panel;
36498         }
36499         
36500         
36501         
36502         
36503         if(this.hasPanel(panel)){
36504             this.showPanel(panel);
36505             return panel;
36506         }
36507         panel.setRegion(this);
36508         this.panels.add(panel);
36509        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
36510             // sinle panel - no tab...?? would it not be better to render it with the tabs,
36511             // and hide them... ???
36512             this.bodyEl.dom.appendChild(panel.getEl().dom);
36513             if(panel.background !== true){
36514                 this.setActivePanel(panel);
36515             }
36516             this.fireEvent("paneladded", this, panel);
36517             return panel;
36518         }
36519         */
36520         if(!this.tabs){
36521             this.initTabs();
36522         }else{
36523             this.initPanelAsTab(panel);
36524         }
36525         
36526         
36527         if(panel.background !== true){
36528             this.tabs.activate(panel.getEl().id);
36529         }
36530         this.fireEvent("paneladded", this, panel);
36531         return panel;
36532     },
36533
36534     /**
36535      * Hides the tab for the specified panel.
36536      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36537      */
36538     hidePanel : function(panel){
36539         if(this.tabs && (panel = this.getPanel(panel))){
36540             this.tabs.hideTab(panel.getEl().id);
36541         }
36542     },
36543
36544     /**
36545      * Unhides the tab for a previously hidden panel.
36546      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36547      */
36548     unhidePanel : function(panel){
36549         if(this.tabs && (panel = this.getPanel(panel))){
36550             this.tabs.unhideTab(panel.getEl().id);
36551         }
36552     },
36553
36554     clearPanels : function(){
36555         while(this.panels.getCount() > 0){
36556              this.remove(this.panels.first());
36557         }
36558     },
36559
36560     /**
36561      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
36562      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
36563      * @param {Boolean} preservePanel Overrides the config preservePanel option
36564      * @return {Roo.ContentPanel} The panel that was removed
36565      */
36566     remove : function(panel, preservePanel)
36567     {
36568         panel = this.getPanel(panel);
36569         if(!panel){
36570             return null;
36571         }
36572         var e = {};
36573         this.fireEvent("beforeremove", this, panel, e);
36574         if(e.cancel === true){
36575             return null;
36576         }
36577         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
36578         var panelId = panel.getId();
36579         this.panels.removeKey(panelId);
36580         if(preservePanel){
36581             document.body.appendChild(panel.getEl().dom);
36582         }
36583         if(this.tabs){
36584             this.tabs.removeTab(panel.getEl().id);
36585         }else if (!preservePanel){
36586             this.bodyEl.dom.removeChild(panel.getEl().dom);
36587         }
36588         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
36589             var p = this.panels.first();
36590             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
36591             tempEl.appendChild(p.getEl().dom);
36592             this.bodyEl.update("");
36593             this.bodyEl.dom.appendChild(p.getEl().dom);
36594             tempEl = null;
36595             this.updateTitle(p.getTitle());
36596             this.tabs = null;
36597             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
36598             this.setActivePanel(p);
36599         }
36600         panel.setRegion(null);
36601         if(this.activePanel == panel){
36602             this.activePanel = null;
36603         }
36604         if(this.config.autoDestroy !== false && preservePanel !== true){
36605             try{panel.destroy();}catch(e){}
36606         }
36607         this.fireEvent("panelremoved", this, panel);
36608         return panel;
36609     },
36610
36611     /**
36612      * Returns the TabPanel component used by this region
36613      * @return {Roo.TabPanel}
36614      */
36615     getTabs : function(){
36616         return this.tabs;
36617     },
36618
36619     createTool : function(parentEl, className){
36620         var btn = Roo.DomHelper.append(parentEl, {
36621             tag: "div",
36622             cls: "x-layout-tools-button",
36623             children: [ {
36624                 tag: "div",
36625                 cls: "roo-layout-tools-button-inner " + className,
36626                 html: "&#160;"
36627             }]
36628         }, true);
36629         btn.addClassOnOver("roo-layout-tools-button-over");
36630         return btn;
36631     }
36632 });/*
36633  * Based on:
36634  * Ext JS Library 1.1.1
36635  * Copyright(c) 2006-2007, Ext JS, LLC.
36636  *
36637  * Originally Released Under LGPL - original licence link has changed is not relivant.
36638  *
36639  * Fork - LGPL
36640  * <script type="text/javascript">
36641  */
36642  
36643
36644
36645 /**
36646  * @class Roo.SplitLayoutRegion
36647  * @extends Roo.LayoutRegion
36648  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
36649  */
36650 Roo.bootstrap.layout.Split = function(config){
36651     this.cursor = config.cursor;
36652     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
36653 };
36654
36655 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
36656 {
36657     splitTip : "Drag to resize.",
36658     collapsibleSplitTip : "Drag to resize. Double click to hide.",
36659     useSplitTips : false,
36660
36661     applyConfig : function(config){
36662         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
36663     },
36664     
36665     onRender : function(ctr,pos) {
36666         
36667         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
36668         if(!this.config.split){
36669             return;
36670         }
36671         if(!this.split){
36672             
36673             var splitEl = Roo.DomHelper.append(ctr.dom,  {
36674                             tag: "div",
36675                             id: this.el.id + "-split",
36676                             cls: "roo-layout-split roo-layout-split-"+this.position,
36677                             html: "&#160;"
36678             });
36679             /** The SplitBar for this region 
36680             * @type Roo.SplitBar */
36681             // does not exist yet...
36682             Roo.log([this.position, this.orientation]);
36683             
36684             this.split = new Roo.bootstrap.SplitBar({
36685                 dragElement : splitEl,
36686                 resizingElement: this.el,
36687                 orientation : this.orientation
36688             });
36689             
36690             this.split.on("moved", this.onSplitMove, this);
36691             this.split.useShim = this.config.useShim === true;
36692             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
36693             if(this.useSplitTips){
36694                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
36695             }
36696             //if(config.collapsible){
36697             //    this.split.el.on("dblclick", this.collapse,  this);
36698             //}
36699         }
36700         if(typeof this.config.minSize != "undefined"){
36701             this.split.minSize = this.config.minSize;
36702         }
36703         if(typeof this.config.maxSize != "undefined"){
36704             this.split.maxSize = this.config.maxSize;
36705         }
36706         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
36707             this.hideSplitter();
36708         }
36709         
36710     },
36711
36712     getHMaxSize : function(){
36713          var cmax = this.config.maxSize || 10000;
36714          var center = this.mgr.getRegion("center");
36715          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
36716     },
36717
36718     getVMaxSize : function(){
36719          var cmax = this.config.maxSize || 10000;
36720          var center = this.mgr.getRegion("center");
36721          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36722     },
36723
36724     onSplitMove : function(split, newSize){
36725         this.fireEvent("resized", this, newSize);
36726     },
36727     
36728     /** 
36729      * Returns the {@link Roo.SplitBar} for this region.
36730      * @return {Roo.SplitBar}
36731      */
36732     getSplitBar : function(){
36733         return this.split;
36734     },
36735     
36736     hide : function(){
36737         this.hideSplitter();
36738         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36739     },
36740
36741     hideSplitter : function(){
36742         if(this.split){
36743             this.split.el.setLocation(-2000,-2000);
36744             this.split.el.hide();
36745         }
36746     },
36747
36748     show : function(){
36749         if(this.split){
36750             this.split.el.show();
36751         }
36752         Roo.bootstrap.layout.Split.superclass.show.call(this);
36753     },
36754     
36755     beforeSlide: function(){
36756         if(Roo.isGecko){// firefox overflow auto bug workaround
36757             this.bodyEl.clip();
36758             if(this.tabs) {
36759                 this.tabs.bodyEl.clip();
36760             }
36761             if(this.activePanel){
36762                 this.activePanel.getEl().clip();
36763                 
36764                 if(this.activePanel.beforeSlide){
36765                     this.activePanel.beforeSlide();
36766                 }
36767             }
36768         }
36769     },
36770     
36771     afterSlide : function(){
36772         if(Roo.isGecko){// firefox overflow auto bug workaround
36773             this.bodyEl.unclip();
36774             if(this.tabs) {
36775                 this.tabs.bodyEl.unclip();
36776             }
36777             if(this.activePanel){
36778                 this.activePanel.getEl().unclip();
36779                 if(this.activePanel.afterSlide){
36780                     this.activePanel.afterSlide();
36781                 }
36782             }
36783         }
36784     },
36785
36786     initAutoHide : function(){
36787         if(this.autoHide !== false){
36788             if(!this.autoHideHd){
36789                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36790                 this.autoHideHd = {
36791                     "mouseout": function(e){
36792                         if(!e.within(this.el, true)){
36793                             st.delay(500);
36794                         }
36795                     },
36796                     "mouseover" : function(e){
36797                         st.cancel();
36798                     },
36799                     scope : this
36800                 };
36801             }
36802             this.el.on(this.autoHideHd);
36803         }
36804     },
36805
36806     clearAutoHide : function(){
36807         if(this.autoHide !== false){
36808             this.el.un("mouseout", this.autoHideHd.mouseout);
36809             this.el.un("mouseover", this.autoHideHd.mouseover);
36810         }
36811     },
36812
36813     clearMonitor : function(){
36814         Roo.get(document).un("click", this.slideInIf, this);
36815     },
36816
36817     // these names are backwards but not changed for compat
36818     slideOut : function(){
36819         if(this.isSlid || this.el.hasActiveFx()){
36820             return;
36821         }
36822         this.isSlid = true;
36823         if(this.collapseBtn){
36824             this.collapseBtn.hide();
36825         }
36826         this.closeBtnState = this.closeBtn.getStyle('display');
36827         this.closeBtn.hide();
36828         if(this.stickBtn){
36829             this.stickBtn.show();
36830         }
36831         this.el.show();
36832         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36833         this.beforeSlide();
36834         this.el.setStyle("z-index", 10001);
36835         this.el.slideIn(this.getSlideAnchor(), {
36836             callback: function(){
36837                 this.afterSlide();
36838                 this.initAutoHide();
36839                 Roo.get(document).on("click", this.slideInIf, this);
36840                 this.fireEvent("slideshow", this);
36841             },
36842             scope: this,
36843             block: true
36844         });
36845     },
36846
36847     afterSlideIn : function(){
36848         this.clearAutoHide();
36849         this.isSlid = false;
36850         this.clearMonitor();
36851         this.el.setStyle("z-index", "");
36852         if(this.collapseBtn){
36853             this.collapseBtn.show();
36854         }
36855         this.closeBtn.setStyle('display', this.closeBtnState);
36856         if(this.stickBtn){
36857             this.stickBtn.hide();
36858         }
36859         this.fireEvent("slidehide", this);
36860     },
36861
36862     slideIn : function(cb){
36863         if(!this.isSlid || this.el.hasActiveFx()){
36864             Roo.callback(cb);
36865             return;
36866         }
36867         this.isSlid = false;
36868         this.beforeSlide();
36869         this.el.slideOut(this.getSlideAnchor(), {
36870             callback: function(){
36871                 this.el.setLeftTop(-10000, -10000);
36872                 this.afterSlide();
36873                 this.afterSlideIn();
36874                 Roo.callback(cb);
36875             },
36876             scope: this,
36877             block: true
36878         });
36879     },
36880     
36881     slideInIf : function(e){
36882         if(!e.within(this.el)){
36883             this.slideIn();
36884         }
36885     },
36886
36887     animateCollapse : function(){
36888         this.beforeSlide();
36889         this.el.setStyle("z-index", 20000);
36890         var anchor = this.getSlideAnchor();
36891         this.el.slideOut(anchor, {
36892             callback : function(){
36893                 this.el.setStyle("z-index", "");
36894                 this.collapsedEl.slideIn(anchor, {duration:.3});
36895                 this.afterSlide();
36896                 this.el.setLocation(-10000,-10000);
36897                 this.el.hide();
36898                 this.fireEvent("collapsed", this);
36899             },
36900             scope: this,
36901             block: true
36902         });
36903     },
36904
36905     animateExpand : function(){
36906         this.beforeSlide();
36907         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36908         this.el.setStyle("z-index", 20000);
36909         this.collapsedEl.hide({
36910             duration:.1
36911         });
36912         this.el.slideIn(this.getSlideAnchor(), {
36913             callback : function(){
36914                 this.el.setStyle("z-index", "");
36915                 this.afterSlide();
36916                 if(this.split){
36917                     this.split.el.show();
36918                 }
36919                 this.fireEvent("invalidated", this);
36920                 this.fireEvent("expanded", this);
36921             },
36922             scope: this,
36923             block: true
36924         });
36925     },
36926
36927     anchors : {
36928         "west" : "left",
36929         "east" : "right",
36930         "north" : "top",
36931         "south" : "bottom"
36932     },
36933
36934     sanchors : {
36935         "west" : "l",
36936         "east" : "r",
36937         "north" : "t",
36938         "south" : "b"
36939     },
36940
36941     canchors : {
36942         "west" : "tl-tr",
36943         "east" : "tr-tl",
36944         "north" : "tl-bl",
36945         "south" : "bl-tl"
36946     },
36947
36948     getAnchor : function(){
36949         return this.anchors[this.position];
36950     },
36951
36952     getCollapseAnchor : function(){
36953         return this.canchors[this.position];
36954     },
36955
36956     getSlideAnchor : function(){
36957         return this.sanchors[this.position];
36958     },
36959
36960     getAlignAdj : function(){
36961         var cm = this.cmargins;
36962         switch(this.position){
36963             case "west":
36964                 return [0, 0];
36965             break;
36966             case "east":
36967                 return [0, 0];
36968             break;
36969             case "north":
36970                 return [0, 0];
36971             break;
36972             case "south":
36973                 return [0, 0];
36974             break;
36975         }
36976     },
36977
36978     getExpandAdj : function(){
36979         var c = this.collapsedEl, cm = this.cmargins;
36980         switch(this.position){
36981             case "west":
36982                 return [-(cm.right+c.getWidth()+cm.left), 0];
36983             break;
36984             case "east":
36985                 return [cm.right+c.getWidth()+cm.left, 0];
36986             break;
36987             case "north":
36988                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36989             break;
36990             case "south":
36991                 return [0, cm.top+cm.bottom+c.getHeight()];
36992             break;
36993         }
36994     }
36995 });/*
36996  * Based on:
36997  * Ext JS Library 1.1.1
36998  * Copyright(c) 2006-2007, Ext JS, LLC.
36999  *
37000  * Originally Released Under LGPL - original licence link has changed is not relivant.
37001  *
37002  * Fork - LGPL
37003  * <script type="text/javascript">
37004  */
37005 /*
37006  * These classes are private internal classes
37007  */
37008 Roo.bootstrap.layout.Center = function(config){
37009     config.region = "center";
37010     Roo.bootstrap.layout.Region.call(this, config);
37011     this.visible = true;
37012     this.minWidth = config.minWidth || 20;
37013     this.minHeight = config.minHeight || 20;
37014 };
37015
37016 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
37017     hide : function(){
37018         // center panel can't be hidden
37019     },
37020     
37021     show : function(){
37022         // center panel can't be hidden
37023     },
37024     
37025     getMinWidth: function(){
37026         return this.minWidth;
37027     },
37028     
37029     getMinHeight: function(){
37030         return this.minHeight;
37031     }
37032 });
37033
37034
37035
37036
37037  
37038
37039
37040
37041
37042
37043
37044 Roo.bootstrap.layout.North = function(config)
37045 {
37046     config.region = 'north';
37047     config.cursor = 'n-resize';
37048     
37049     Roo.bootstrap.layout.Split.call(this, config);
37050     
37051     
37052     if(this.split){
37053         this.split.placement = Roo.bootstrap.SplitBar.TOP;
37054         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37055         this.split.el.addClass("roo-layout-split-v");
37056     }
37057     var size = config.initialSize || config.height;
37058     if(typeof size != "undefined"){
37059         this.el.setHeight(size);
37060     }
37061 };
37062 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
37063 {
37064     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37065     
37066     
37067     
37068     getBox : function(){
37069         if(this.collapsed){
37070             return this.collapsedEl.getBox();
37071         }
37072         var box = this.el.getBox();
37073         if(this.split){
37074             box.height += this.split.el.getHeight();
37075         }
37076         return box;
37077     },
37078     
37079     updateBox : function(box){
37080         if(this.split && !this.collapsed){
37081             box.height -= this.split.el.getHeight();
37082             this.split.el.setLeft(box.x);
37083             this.split.el.setTop(box.y+box.height);
37084             this.split.el.setWidth(box.width);
37085         }
37086         if(this.collapsed){
37087             this.updateBody(box.width, null);
37088         }
37089         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37090     }
37091 });
37092
37093
37094
37095
37096
37097 Roo.bootstrap.layout.South = function(config){
37098     config.region = 'south';
37099     config.cursor = 's-resize';
37100     Roo.bootstrap.layout.Split.call(this, config);
37101     if(this.split){
37102         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
37103         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
37104         this.split.el.addClass("roo-layout-split-v");
37105     }
37106     var size = config.initialSize || config.height;
37107     if(typeof size != "undefined"){
37108         this.el.setHeight(size);
37109     }
37110 };
37111
37112 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
37113     orientation: Roo.bootstrap.SplitBar.VERTICAL,
37114     getBox : function(){
37115         if(this.collapsed){
37116             return this.collapsedEl.getBox();
37117         }
37118         var box = this.el.getBox();
37119         if(this.split){
37120             var sh = this.split.el.getHeight();
37121             box.height += sh;
37122             box.y -= sh;
37123         }
37124         return box;
37125     },
37126     
37127     updateBox : function(box){
37128         if(this.split && !this.collapsed){
37129             var sh = this.split.el.getHeight();
37130             box.height -= sh;
37131             box.y += sh;
37132             this.split.el.setLeft(box.x);
37133             this.split.el.setTop(box.y-sh);
37134             this.split.el.setWidth(box.width);
37135         }
37136         if(this.collapsed){
37137             this.updateBody(box.width, null);
37138         }
37139         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37140     }
37141 });
37142
37143 Roo.bootstrap.layout.East = function(config){
37144     config.region = "east";
37145     config.cursor = "e-resize";
37146     Roo.bootstrap.layout.Split.call(this, config);
37147     if(this.split){
37148         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
37149         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37150         this.split.el.addClass("roo-layout-split-h");
37151     }
37152     var size = config.initialSize || config.width;
37153     if(typeof size != "undefined"){
37154         this.el.setWidth(size);
37155     }
37156 };
37157 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
37158     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37159     getBox : function(){
37160         if(this.collapsed){
37161             return this.collapsedEl.getBox();
37162         }
37163         var box = this.el.getBox();
37164         if(this.split){
37165             var sw = this.split.el.getWidth();
37166             box.width += sw;
37167             box.x -= sw;
37168         }
37169         return box;
37170     },
37171
37172     updateBox : function(box){
37173         if(this.split && !this.collapsed){
37174             var sw = this.split.el.getWidth();
37175             box.width -= sw;
37176             this.split.el.setLeft(box.x);
37177             this.split.el.setTop(box.y);
37178             this.split.el.setHeight(box.height);
37179             box.x += sw;
37180         }
37181         if(this.collapsed){
37182             this.updateBody(null, box.height);
37183         }
37184         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37185     }
37186 });
37187
37188 Roo.bootstrap.layout.West = function(config){
37189     config.region = "west";
37190     config.cursor = "w-resize";
37191     
37192     Roo.bootstrap.layout.Split.call(this, config);
37193     if(this.split){
37194         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
37195         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
37196         this.split.el.addClass("roo-layout-split-h");
37197     }
37198     
37199 };
37200 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
37201     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
37202     
37203     onRender: function(ctr, pos)
37204     {
37205         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
37206         var size = this.config.initialSize || this.config.width;
37207         if(typeof size != "undefined"){
37208             this.el.setWidth(size);
37209         }
37210     },
37211     
37212     getBox : function(){
37213         if(this.collapsed){
37214             return this.collapsedEl.getBox();
37215         }
37216         var box = this.el.getBox();
37217         if(this.split){
37218             box.width += this.split.el.getWidth();
37219         }
37220         return box;
37221     },
37222     
37223     updateBox : function(box){
37224         if(this.split && !this.collapsed){
37225             var sw = this.split.el.getWidth();
37226             box.width -= sw;
37227             this.split.el.setLeft(box.x+box.width);
37228             this.split.el.setTop(box.y);
37229             this.split.el.setHeight(box.height);
37230         }
37231         if(this.collapsed){
37232             this.updateBody(null, box.height);
37233         }
37234         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
37235     }
37236 });Roo.namespace("Roo.bootstrap.panel");/*
37237  * Based on:
37238  * Ext JS Library 1.1.1
37239  * Copyright(c) 2006-2007, Ext JS, LLC.
37240  *
37241  * Originally Released Under LGPL - original licence link has changed is not relivant.
37242  *
37243  * Fork - LGPL
37244  * <script type="text/javascript">
37245  */
37246 /**
37247  * @class Roo.ContentPanel
37248  * @extends Roo.util.Observable
37249  * A basic ContentPanel element.
37250  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
37251  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
37252  * @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
37253  * @cfg {Boolean}   closable      True if the panel can be closed/removed
37254  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
37255  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
37256  * @cfg {Toolbar}   toolbar       A toolbar for this panel
37257  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
37258  * @cfg {String} title          The title for this panel
37259  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
37260  * @cfg {String} url            Calls {@link #setUrl} with this value
37261  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
37262  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
37263  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
37264  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
37265  * @cfg {Boolean} badges render the badges
37266
37267  * @constructor
37268  * Create a new ContentPanel.
37269  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
37270  * @param {String/Object} config A string to set only the title or a config object
37271  * @param {String} content (optional) Set the HTML content for this panel
37272  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
37273  */
37274 Roo.bootstrap.panel.Content = function( config){
37275     
37276     this.tpl = config.tpl || false;
37277     
37278     var el = config.el;
37279     var content = config.content;
37280
37281     if(config.autoCreate){ // xtype is available if this is called from factory
37282         el = Roo.id();
37283     }
37284     this.el = Roo.get(el);
37285     if(!this.el && config && config.autoCreate){
37286         if(typeof config.autoCreate == "object"){
37287             if(!config.autoCreate.id){
37288                 config.autoCreate.id = config.id||el;
37289             }
37290             this.el = Roo.DomHelper.append(document.body,
37291                         config.autoCreate, true);
37292         }else{
37293             var elcfg =  {   tag: "div",
37294                             cls: "roo-layout-inactive-content",
37295                             id: config.id||el
37296                             };
37297             if (config.html) {
37298                 elcfg.html = config.html;
37299                 
37300             }
37301                         
37302             this.el = Roo.DomHelper.append(document.body, elcfg , true);
37303         }
37304     } 
37305     this.closable = false;
37306     this.loaded = false;
37307     this.active = false;
37308    
37309       
37310     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
37311         
37312         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
37313         
37314         this.wrapEl = this.el; //this.el.wrap();
37315         var ti = [];
37316         if (config.toolbar.items) {
37317             ti = config.toolbar.items ;
37318             delete config.toolbar.items ;
37319         }
37320         
37321         var nitems = [];
37322         this.toolbar.render(this.wrapEl, 'before');
37323         for(var i =0;i < ti.length;i++) {
37324           //  Roo.log(['add child', items[i]]);
37325             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37326         }
37327         this.toolbar.items = nitems;
37328         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
37329         delete config.toolbar;
37330         
37331     }
37332     /*
37333     // xtype created footer. - not sure if will work as we normally have to render first..
37334     if (this.footer && !this.footer.el && this.footer.xtype) {
37335         if (!this.wrapEl) {
37336             this.wrapEl = this.el.wrap();
37337         }
37338     
37339         this.footer.container = this.wrapEl.createChild();
37340          
37341         this.footer = Roo.factory(this.footer, Roo);
37342         
37343     }
37344     */
37345     
37346      if(typeof config == "string"){
37347         this.title = config;
37348     }else{
37349         Roo.apply(this, config);
37350     }
37351     
37352     if(this.resizeEl){
37353         this.resizeEl = Roo.get(this.resizeEl, true);
37354     }else{
37355         this.resizeEl = this.el;
37356     }
37357     // handle view.xtype
37358     
37359  
37360     
37361     
37362     this.addEvents({
37363         /**
37364          * @event activate
37365          * Fires when this panel is activated. 
37366          * @param {Roo.ContentPanel} this
37367          */
37368         "activate" : true,
37369         /**
37370          * @event deactivate
37371          * Fires when this panel is activated. 
37372          * @param {Roo.ContentPanel} this
37373          */
37374         "deactivate" : true,
37375
37376         /**
37377          * @event resize
37378          * Fires when this panel is resized if fitToFrame is true.
37379          * @param {Roo.ContentPanel} this
37380          * @param {Number} width The width after any component adjustments
37381          * @param {Number} height The height after any component adjustments
37382          */
37383         "resize" : true,
37384         
37385          /**
37386          * @event render
37387          * Fires when this tab is created
37388          * @param {Roo.ContentPanel} this
37389          */
37390         "render" : true
37391         
37392         
37393         
37394     });
37395     
37396
37397     
37398     
37399     if(this.autoScroll){
37400         this.resizeEl.setStyle("overflow", "auto");
37401     } else {
37402         // fix randome scrolling
37403         //this.el.on('scroll', function() {
37404         //    Roo.log('fix random scolling');
37405         //    this.scrollTo('top',0); 
37406         //});
37407     }
37408     content = content || this.content;
37409     if(content){
37410         this.setContent(content);
37411     }
37412     if(config && config.url){
37413         this.setUrl(this.url, this.params, this.loadOnce);
37414     }
37415     
37416     
37417     
37418     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
37419     
37420     if (this.view && typeof(this.view.xtype) != 'undefined') {
37421         this.view.el = this.el.appendChild(document.createElement("div"));
37422         this.view = Roo.factory(this.view); 
37423         this.view.render  &&  this.view.render(false, '');  
37424     }
37425     
37426     
37427     this.fireEvent('render', this);
37428 };
37429
37430 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
37431     
37432     tabTip : '',
37433     
37434     setRegion : function(region){
37435         this.region = region;
37436         this.setActiveClass(region && !this.background);
37437     },
37438     
37439     
37440     setActiveClass: function(state)
37441     {
37442         if(state){
37443            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
37444            this.el.setStyle('position','relative');
37445         }else{
37446            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
37447            this.el.setStyle('position', 'absolute');
37448         } 
37449     },
37450     
37451     /**
37452      * Returns the toolbar for this Panel if one was configured. 
37453      * @return {Roo.Toolbar} 
37454      */
37455     getToolbar : function(){
37456         return this.toolbar;
37457     },
37458     
37459     setActiveState : function(active)
37460     {
37461         this.active = active;
37462         this.setActiveClass(active);
37463         if(!active){
37464             if(this.fireEvent("deactivate", this) === false){
37465                 return false;
37466             }
37467             return true;
37468         }
37469         this.fireEvent("activate", this);
37470         return true;
37471     },
37472     /**
37473      * Updates this panel's element
37474      * @param {String} content The new content
37475      * @param {Boolean} loadScripts (optional) true to look for and process scripts
37476     */
37477     setContent : function(content, loadScripts){
37478         this.el.update(content, loadScripts);
37479     },
37480
37481     ignoreResize : function(w, h){
37482         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
37483             return true;
37484         }else{
37485             this.lastSize = {width: w, height: h};
37486             return false;
37487         }
37488     },
37489     /**
37490      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
37491      * @return {Roo.UpdateManager} The UpdateManager
37492      */
37493     getUpdateManager : function(){
37494         return this.el.getUpdateManager();
37495     },
37496      /**
37497      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
37498      * @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:
37499 <pre><code>
37500 panel.load({
37501     url: "your-url.php",
37502     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
37503     callback: yourFunction,
37504     scope: yourObject, //(optional scope)
37505     discardUrl: false,
37506     nocache: false,
37507     text: "Loading...",
37508     timeout: 30,
37509     scripts: false
37510 });
37511 </code></pre>
37512      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
37513      * 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.
37514      * @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}
37515      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
37516      * @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.
37517      * @return {Roo.ContentPanel} this
37518      */
37519     load : function(){
37520         var um = this.el.getUpdateManager();
37521         um.update.apply(um, arguments);
37522         return this;
37523     },
37524
37525
37526     /**
37527      * 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.
37528      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
37529      * @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)
37530      * @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)
37531      * @return {Roo.UpdateManager} The UpdateManager
37532      */
37533     setUrl : function(url, params, loadOnce){
37534         if(this.refreshDelegate){
37535             this.removeListener("activate", this.refreshDelegate);
37536         }
37537         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37538         this.on("activate", this.refreshDelegate);
37539         return this.el.getUpdateManager();
37540     },
37541     
37542     _handleRefresh : function(url, params, loadOnce){
37543         if(!loadOnce || !this.loaded){
37544             var updater = this.el.getUpdateManager();
37545             updater.update(url, params, this._setLoaded.createDelegate(this));
37546         }
37547     },
37548     
37549     _setLoaded : function(){
37550         this.loaded = true;
37551     }, 
37552     
37553     /**
37554      * Returns this panel's id
37555      * @return {String} 
37556      */
37557     getId : function(){
37558         return this.el.id;
37559     },
37560     
37561     /** 
37562      * Returns this panel's element - used by regiosn to add.
37563      * @return {Roo.Element} 
37564      */
37565     getEl : function(){
37566         return this.wrapEl || this.el;
37567     },
37568     
37569    
37570     
37571     adjustForComponents : function(width, height)
37572     {
37573         //Roo.log('adjustForComponents ');
37574         if(this.resizeEl != this.el){
37575             width -= this.el.getFrameWidth('lr');
37576             height -= this.el.getFrameWidth('tb');
37577         }
37578         if(this.toolbar){
37579             var te = this.toolbar.getEl();
37580             te.setWidth(width);
37581             height -= te.getHeight();
37582         }
37583         if(this.footer){
37584             var te = this.footer.getEl();
37585             te.setWidth(width);
37586             height -= te.getHeight();
37587         }
37588         
37589         
37590         if(this.adjustments){
37591             width += this.adjustments[0];
37592             height += this.adjustments[1];
37593         }
37594         return {"width": width, "height": height};
37595     },
37596     
37597     setSize : function(width, height){
37598         if(this.fitToFrame && !this.ignoreResize(width, height)){
37599             if(this.fitContainer && this.resizeEl != this.el){
37600                 this.el.setSize(width, height);
37601             }
37602             var size = this.adjustForComponents(width, height);
37603             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
37604             this.fireEvent('resize', this, size.width, size.height);
37605         }
37606     },
37607     
37608     /**
37609      * Returns this panel's title
37610      * @return {String} 
37611      */
37612     getTitle : function(){
37613         
37614         if (typeof(this.title) != 'object') {
37615             return this.title;
37616         }
37617         
37618         var t = '';
37619         for (var k in this.title) {
37620             if (!this.title.hasOwnProperty(k)) {
37621                 continue;
37622             }
37623             
37624             if (k.indexOf('-') >= 0) {
37625                 var s = k.split('-');
37626                 for (var i = 0; i<s.length; i++) {
37627                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
37628                 }
37629             } else {
37630                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
37631             }
37632         }
37633         return t;
37634     },
37635     
37636     /**
37637      * Set this panel's title
37638      * @param {String} title
37639      */
37640     setTitle : function(title){
37641         this.title = title;
37642         if(this.region){
37643             this.region.updatePanelTitle(this, title);
37644         }
37645     },
37646     
37647     /**
37648      * Returns true is this panel was configured to be closable
37649      * @return {Boolean} 
37650      */
37651     isClosable : function(){
37652         return this.closable;
37653     },
37654     
37655     beforeSlide : function(){
37656         this.el.clip();
37657         this.resizeEl.clip();
37658     },
37659     
37660     afterSlide : function(){
37661         this.el.unclip();
37662         this.resizeEl.unclip();
37663     },
37664     
37665     /**
37666      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
37667      *   Will fail silently if the {@link #setUrl} method has not been called.
37668      *   This does not activate the panel, just updates its content.
37669      */
37670     refresh : function(){
37671         if(this.refreshDelegate){
37672            this.loaded = false;
37673            this.refreshDelegate();
37674         }
37675     },
37676     
37677     /**
37678      * Destroys this panel
37679      */
37680     destroy : function(){
37681         this.el.removeAllListeners();
37682         var tempEl = document.createElement("span");
37683         tempEl.appendChild(this.el.dom);
37684         tempEl.innerHTML = "";
37685         this.el.remove();
37686         this.el = null;
37687     },
37688     
37689     /**
37690      * form - if the content panel contains a form - this is a reference to it.
37691      * @type {Roo.form.Form}
37692      */
37693     form : false,
37694     /**
37695      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
37696      *    This contains a reference to it.
37697      * @type {Roo.View}
37698      */
37699     view : false,
37700     
37701       /**
37702      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
37703      * <pre><code>
37704
37705 layout.addxtype({
37706        xtype : 'Form',
37707        items: [ .... ]
37708    }
37709 );
37710
37711 </code></pre>
37712      * @param {Object} cfg Xtype definition of item to add.
37713      */
37714     
37715     
37716     getChildContainer: function () {
37717         return this.getEl();
37718     }
37719     
37720     
37721     /*
37722         var  ret = new Roo.factory(cfg);
37723         return ret;
37724         
37725         
37726         // add form..
37727         if (cfg.xtype.match(/^Form$/)) {
37728             
37729             var el;
37730             //if (this.footer) {
37731             //    el = this.footer.container.insertSibling(false, 'before');
37732             //} else {
37733                 el = this.el.createChild();
37734             //}
37735
37736             this.form = new  Roo.form.Form(cfg);
37737             
37738             
37739             if ( this.form.allItems.length) {
37740                 this.form.render(el.dom);
37741             }
37742             return this.form;
37743         }
37744         // should only have one of theses..
37745         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37746             // views.. should not be just added - used named prop 'view''
37747             
37748             cfg.el = this.el.appendChild(document.createElement("div"));
37749             // factory?
37750             
37751             var ret = new Roo.factory(cfg);
37752              
37753              ret.render && ret.render(false, ''); // render blank..
37754             this.view = ret;
37755             return ret;
37756         }
37757         return false;
37758     }
37759     \*/
37760 });
37761  
37762 /**
37763  * @class Roo.bootstrap.panel.Grid
37764  * @extends Roo.bootstrap.panel.Content
37765  * @constructor
37766  * Create a new GridPanel.
37767  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37768  * @param {Object} config A the config object
37769   
37770  */
37771
37772
37773
37774 Roo.bootstrap.panel.Grid = function(config)
37775 {
37776     
37777       
37778     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37779         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37780
37781     config.el = this.wrapper;
37782     //this.el = this.wrapper;
37783     
37784       if (config.container) {
37785         // ctor'ed from a Border/panel.grid
37786         
37787         
37788         this.wrapper.setStyle("overflow", "hidden");
37789         this.wrapper.addClass('roo-grid-container');
37790
37791     }
37792     
37793     
37794     if(config.toolbar){
37795         var tool_el = this.wrapper.createChild();    
37796         this.toolbar = Roo.factory(config.toolbar);
37797         var ti = [];
37798         if (config.toolbar.items) {
37799             ti = config.toolbar.items ;
37800             delete config.toolbar.items ;
37801         }
37802         
37803         var nitems = [];
37804         this.toolbar.render(tool_el);
37805         for(var i =0;i < ti.length;i++) {
37806           //  Roo.log(['add child', items[i]]);
37807             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37808         }
37809         this.toolbar.items = nitems;
37810         
37811         delete config.toolbar;
37812     }
37813     
37814     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37815     config.grid.scrollBody = true;;
37816     config.grid.monitorWindowResize = false; // turn off autosizing
37817     config.grid.autoHeight = false;
37818     config.grid.autoWidth = false;
37819     
37820     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37821     
37822     if (config.background) {
37823         // render grid on panel activation (if panel background)
37824         this.on('activate', function(gp) {
37825             if (!gp.grid.rendered) {
37826                 gp.grid.render(this.wrapper);
37827                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37828             }
37829         });
37830             
37831     } else {
37832         this.grid.render(this.wrapper);
37833         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37834
37835     }
37836     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37837     // ??? needed ??? config.el = this.wrapper;
37838     
37839     
37840     
37841   
37842     // xtype created footer. - not sure if will work as we normally have to render first..
37843     if (this.footer && !this.footer.el && this.footer.xtype) {
37844         
37845         var ctr = this.grid.getView().getFooterPanel(true);
37846         this.footer.dataSource = this.grid.dataSource;
37847         this.footer = Roo.factory(this.footer, Roo);
37848         this.footer.render(ctr);
37849         
37850     }
37851     
37852     
37853     
37854     
37855      
37856 };
37857
37858 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37859     getId : function(){
37860         return this.grid.id;
37861     },
37862     
37863     /**
37864      * Returns the grid for this panel
37865      * @return {Roo.bootstrap.Table} 
37866      */
37867     getGrid : function(){
37868         return this.grid;    
37869     },
37870     
37871     setSize : function(width, height){
37872         if(!this.ignoreResize(width, height)){
37873             var grid = this.grid;
37874             var size = this.adjustForComponents(width, height);
37875             var gridel = grid.getGridEl();
37876             gridel.setSize(size.width, size.height);
37877             /*
37878             var thd = grid.getGridEl().select('thead',true).first();
37879             var tbd = grid.getGridEl().select('tbody', true).first();
37880             if (tbd) {
37881                 tbd.setSize(width, height - thd.getHeight());
37882             }
37883             */
37884             grid.autoSize();
37885         }
37886     },
37887      
37888     
37889     
37890     beforeSlide : function(){
37891         this.grid.getView().scroller.clip();
37892     },
37893     
37894     afterSlide : function(){
37895         this.grid.getView().scroller.unclip();
37896     },
37897     
37898     destroy : function(){
37899         this.grid.destroy();
37900         delete this.grid;
37901         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37902     }
37903 });
37904
37905 /**
37906  * @class Roo.bootstrap.panel.Nest
37907  * @extends Roo.bootstrap.panel.Content
37908  * @constructor
37909  * Create a new Panel, that can contain a layout.Border.
37910  * 
37911  * 
37912  * @param {Roo.BorderLayout} layout The layout for this panel
37913  * @param {String/Object} config A string to set only the title or a config object
37914  */
37915 Roo.bootstrap.panel.Nest = function(config)
37916 {
37917     // construct with only one argument..
37918     /* FIXME - implement nicer consturctors
37919     if (layout.layout) {
37920         config = layout;
37921         layout = config.layout;
37922         delete config.layout;
37923     }
37924     if (layout.xtype && !layout.getEl) {
37925         // then layout needs constructing..
37926         layout = Roo.factory(layout, Roo);
37927     }
37928     */
37929     
37930     config.el =  config.layout.getEl();
37931     
37932     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37933     
37934     config.layout.monitorWindowResize = false; // turn off autosizing
37935     this.layout = config.layout;
37936     this.layout.getEl().addClass("roo-layout-nested-layout");
37937     this.layout.parent = this;
37938     
37939     
37940     
37941     
37942 };
37943
37944 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37945
37946     setSize : function(width, height){
37947         if(!this.ignoreResize(width, height)){
37948             var size = this.adjustForComponents(width, height);
37949             var el = this.layout.getEl();
37950             if (size.height < 1) {
37951                 el.setWidth(size.width);   
37952             } else {
37953                 el.setSize(size.width, size.height);
37954             }
37955             var touch = el.dom.offsetWidth;
37956             this.layout.layout();
37957             // ie requires a double layout on the first pass
37958             if(Roo.isIE && !this.initialized){
37959                 this.initialized = true;
37960                 this.layout.layout();
37961             }
37962         }
37963     },
37964     
37965     // activate all subpanels if not currently active..
37966     
37967     setActiveState : function(active){
37968         this.active = active;
37969         this.setActiveClass(active);
37970         
37971         if(!active){
37972             this.fireEvent("deactivate", this);
37973             return;
37974         }
37975         
37976         this.fireEvent("activate", this);
37977         // not sure if this should happen before or after..
37978         if (!this.layout) {
37979             return; // should not happen..
37980         }
37981         var reg = false;
37982         for (var r in this.layout.regions) {
37983             reg = this.layout.getRegion(r);
37984             if (reg.getActivePanel()) {
37985                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37986                 reg.setActivePanel(reg.getActivePanel());
37987                 continue;
37988             }
37989             if (!reg.panels.length) {
37990                 continue;
37991             }
37992             reg.showPanel(reg.getPanel(0));
37993         }
37994         
37995         
37996         
37997         
37998     },
37999     
38000     /**
38001      * Returns the nested BorderLayout for this panel
38002      * @return {Roo.BorderLayout} 
38003      */
38004     getLayout : function(){
38005         return this.layout;
38006     },
38007     
38008      /**
38009      * Adds a xtype elements to the layout of the nested panel
38010      * <pre><code>
38011
38012 panel.addxtype({
38013        xtype : 'ContentPanel',
38014        region: 'west',
38015        items: [ .... ]
38016    }
38017 );
38018
38019 panel.addxtype({
38020         xtype : 'NestedLayoutPanel',
38021         region: 'west',
38022         layout: {
38023            center: { },
38024            west: { }   
38025         },
38026         items : [ ... list of content panels or nested layout panels.. ]
38027    }
38028 );
38029 </code></pre>
38030      * @param {Object} cfg Xtype definition of item to add.
38031      */
38032     addxtype : function(cfg) {
38033         return this.layout.addxtype(cfg);
38034     
38035     }
38036 });/*
38037  * Based on:
38038  * Ext JS Library 1.1.1
38039  * Copyright(c) 2006-2007, Ext JS, LLC.
38040  *
38041  * Originally Released Under LGPL - original licence link has changed is not relivant.
38042  *
38043  * Fork - LGPL
38044  * <script type="text/javascript">
38045  */
38046 /**
38047  * @class Roo.TabPanel
38048  * @extends Roo.util.Observable
38049  * A lightweight tab container.
38050  * <br><br>
38051  * Usage:
38052  * <pre><code>
38053 // basic tabs 1, built from existing content
38054 var tabs = new Roo.TabPanel("tabs1");
38055 tabs.addTab("script", "View Script");
38056 tabs.addTab("markup", "View Markup");
38057 tabs.activate("script");
38058
38059 // more advanced tabs, built from javascript
38060 var jtabs = new Roo.TabPanel("jtabs");
38061 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
38062
38063 // set up the UpdateManager
38064 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
38065 var updater = tab2.getUpdateManager();
38066 updater.setDefaultUrl("ajax1.htm");
38067 tab2.on('activate', updater.refresh, updater, true);
38068
38069 // Use setUrl for Ajax loading
38070 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
38071 tab3.setUrl("ajax2.htm", null, true);
38072
38073 // Disabled tab
38074 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
38075 tab4.disable();
38076
38077 jtabs.activate("jtabs-1");
38078  * </code></pre>
38079  * @constructor
38080  * Create a new TabPanel.
38081  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
38082  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
38083  */
38084 Roo.bootstrap.panel.Tabs = function(config){
38085     /**
38086     * The container element for this TabPanel.
38087     * @type Roo.Element
38088     */
38089     this.el = Roo.get(config.el);
38090     delete config.el;
38091     if(config){
38092         if(typeof config == "boolean"){
38093             this.tabPosition = config ? "bottom" : "top";
38094         }else{
38095             Roo.apply(this, config);
38096         }
38097     }
38098     
38099     if(this.tabPosition == "bottom"){
38100         // if tabs are at the bottom = create the body first.
38101         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38102         this.el.addClass("roo-tabs-bottom");
38103     }
38104     // next create the tabs holders
38105     
38106     if (this.tabPosition == "west"){
38107         
38108         var reg = this.region; // fake it..
38109         while (reg) {
38110             if (!reg.mgr.parent) {
38111                 break;
38112             }
38113             reg = reg.mgr.parent.region;
38114         }
38115         Roo.log("got nest?");
38116         Roo.log(reg);
38117         if (reg.mgr.getRegion('west')) {
38118             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
38119             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
38120             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38121             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38122             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38123         
38124             
38125         }
38126         
38127         
38128     } else {
38129      
38130         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
38131         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
38132         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
38133         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
38134     }
38135     
38136     
38137     if(Roo.isIE){
38138         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
38139     }
38140     
38141     // finally - if tabs are at the top, then create the body last..
38142     if(this.tabPosition != "bottom"){
38143         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
38144          * @type Roo.Element
38145          */
38146         this.bodyEl = Roo.get(this.createBody(this.el.dom));
38147         this.el.addClass("roo-tabs-top");
38148     }
38149     this.items = [];
38150
38151     this.bodyEl.setStyle("position", "relative");
38152
38153     this.active = null;
38154     this.activateDelegate = this.activate.createDelegate(this);
38155
38156     this.addEvents({
38157         /**
38158          * @event tabchange
38159          * Fires when the active tab changes
38160          * @param {Roo.TabPanel} this
38161          * @param {Roo.TabPanelItem} activePanel The new active tab
38162          */
38163         "tabchange": true,
38164         /**
38165          * @event beforetabchange
38166          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
38167          * @param {Roo.TabPanel} this
38168          * @param {Object} e Set cancel to true on this object to cancel the tab change
38169          * @param {Roo.TabPanelItem} tab The tab being changed to
38170          */
38171         "beforetabchange" : true
38172     });
38173
38174     Roo.EventManager.onWindowResize(this.onResize, this);
38175     this.cpad = this.el.getPadding("lr");
38176     this.hiddenCount = 0;
38177
38178
38179     // toolbar on the tabbar support...
38180     if (this.toolbar) {
38181         alert("no toolbar support yet");
38182         this.toolbar  = false;
38183         /*
38184         var tcfg = this.toolbar;
38185         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
38186         this.toolbar = new Roo.Toolbar(tcfg);
38187         if (Roo.isSafari) {
38188             var tbl = tcfg.container.child('table', true);
38189             tbl.setAttribute('width', '100%');
38190         }
38191         */
38192         
38193     }
38194    
38195
38196
38197     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
38198 };
38199
38200 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
38201     /*
38202      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
38203      */
38204     tabPosition : "top",
38205     /*
38206      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
38207      */
38208     currentTabWidth : 0,
38209     /*
38210      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
38211      */
38212     minTabWidth : 40,
38213     /*
38214      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
38215      */
38216     maxTabWidth : 250,
38217     /*
38218      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
38219      */
38220     preferredTabWidth : 175,
38221     /*
38222      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
38223      */
38224     resizeTabs : false,
38225     /*
38226      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
38227      */
38228     monitorResize : true,
38229     /*
38230      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
38231      */
38232     toolbar : false,  // set by caller..
38233     
38234     region : false, /// set by caller
38235     
38236     disableTooltips : true, // not used yet...
38237
38238     /**
38239      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
38240      * @param {String} id The id of the div to use <b>or create</b>
38241      * @param {String} text The text for the tab
38242      * @param {String} content (optional) Content to put in the TabPanelItem body
38243      * @param {Boolean} closable (optional) True to create a close icon on the tab
38244      * @return {Roo.TabPanelItem} The created TabPanelItem
38245      */
38246     addTab : function(id, text, content, closable, tpl)
38247     {
38248         var item = new Roo.bootstrap.panel.TabItem({
38249             panel: this,
38250             id : id,
38251             text : text,
38252             closable : closable,
38253             tpl : tpl
38254         });
38255         this.addTabItem(item);
38256         if(content){
38257             item.setContent(content);
38258         }
38259         return item;
38260     },
38261
38262     /**
38263      * Returns the {@link Roo.TabPanelItem} with the specified id/index
38264      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
38265      * @return {Roo.TabPanelItem}
38266      */
38267     getTab : function(id){
38268         return this.items[id];
38269     },
38270
38271     /**
38272      * Hides the {@link Roo.TabPanelItem} with the specified id/index
38273      * @param {String/Number} id The id or index of the TabPanelItem to hide.
38274      */
38275     hideTab : function(id){
38276         var t = this.items[id];
38277         if(!t.isHidden()){
38278            t.setHidden(true);
38279            this.hiddenCount++;
38280            this.autoSizeTabs();
38281         }
38282     },
38283
38284     /**
38285      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
38286      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
38287      */
38288     unhideTab : function(id){
38289         var t = this.items[id];
38290         if(t.isHidden()){
38291            t.setHidden(false);
38292            this.hiddenCount--;
38293            this.autoSizeTabs();
38294         }
38295     },
38296
38297     /**
38298      * Adds an existing {@link Roo.TabPanelItem}.
38299      * @param {Roo.TabPanelItem} item The TabPanelItem to add
38300      */
38301     addTabItem : function(item)
38302     {
38303         this.items[item.id] = item;
38304         this.items.push(item);
38305         this.autoSizeTabs();
38306       //  if(this.resizeTabs){
38307     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
38308   //         this.autoSizeTabs();
38309 //        }else{
38310 //            item.autoSize();
38311        // }
38312     },
38313
38314     /**
38315      * Removes a {@link Roo.TabPanelItem}.
38316      * @param {String/Number} id The id or index of the TabPanelItem to remove.
38317      */
38318     removeTab : function(id){
38319         var items = this.items;
38320         var tab = items[id];
38321         if(!tab) { return; }
38322         var index = items.indexOf(tab);
38323         if(this.active == tab && items.length > 1){
38324             var newTab = this.getNextAvailable(index);
38325             if(newTab) {
38326                 newTab.activate();
38327             }
38328         }
38329         this.stripEl.dom.removeChild(tab.pnode.dom);
38330         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
38331             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
38332         }
38333         items.splice(index, 1);
38334         delete this.items[tab.id];
38335         tab.fireEvent("close", tab);
38336         tab.purgeListeners();
38337         this.autoSizeTabs();
38338     },
38339
38340     getNextAvailable : function(start){
38341         var items = this.items;
38342         var index = start;
38343         // look for a next tab that will slide over to
38344         // replace the one being removed
38345         while(index < items.length){
38346             var item = items[++index];
38347             if(item && !item.isHidden()){
38348                 return item;
38349             }
38350         }
38351         // if one isn't found select the previous tab (on the left)
38352         index = start;
38353         while(index >= 0){
38354             var item = items[--index];
38355             if(item && !item.isHidden()){
38356                 return item;
38357             }
38358         }
38359         return null;
38360     },
38361
38362     /**
38363      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
38364      * @param {String/Number} id The id or index of the TabPanelItem to disable.
38365      */
38366     disableTab : function(id){
38367         var tab = this.items[id];
38368         if(tab && this.active != tab){
38369             tab.disable();
38370         }
38371     },
38372
38373     /**
38374      * Enables a {@link Roo.TabPanelItem} that is disabled.
38375      * @param {String/Number} id The id or index of the TabPanelItem to enable.
38376      */
38377     enableTab : function(id){
38378         var tab = this.items[id];
38379         tab.enable();
38380     },
38381
38382     /**
38383      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
38384      * @param {String/Number} id The id or index of the TabPanelItem to activate.
38385      * @return {Roo.TabPanelItem} The TabPanelItem.
38386      */
38387     activate : function(id)
38388     {
38389         //Roo.log('activite:'  + id);
38390         
38391         var tab = this.items[id];
38392         if(!tab){
38393             return null;
38394         }
38395         if(tab == this.active || tab.disabled){
38396             return tab;
38397         }
38398         var e = {};
38399         this.fireEvent("beforetabchange", this, e, tab);
38400         if(e.cancel !== true && !tab.disabled){
38401             if(this.active){
38402                 this.active.hide();
38403             }
38404             this.active = this.items[id];
38405             this.active.show();
38406             this.fireEvent("tabchange", this, this.active);
38407         }
38408         return tab;
38409     },
38410
38411     /**
38412      * Gets the active {@link Roo.TabPanelItem}.
38413      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
38414      */
38415     getActiveTab : function(){
38416         return this.active;
38417     },
38418
38419     /**
38420      * Updates the tab body element to fit the height of the container element
38421      * for overflow scrolling
38422      * @param {Number} targetHeight (optional) Override the starting height from the elements height
38423      */
38424     syncHeight : function(targetHeight){
38425         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38426         var bm = this.bodyEl.getMargins();
38427         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
38428         this.bodyEl.setHeight(newHeight);
38429         return newHeight;
38430     },
38431
38432     onResize : function(){
38433         if(this.monitorResize){
38434             this.autoSizeTabs();
38435         }
38436     },
38437
38438     /**
38439      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
38440      */
38441     beginUpdate : function(){
38442         this.updating = true;
38443     },
38444
38445     /**
38446      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
38447      */
38448     endUpdate : function(){
38449         this.updating = false;
38450         this.autoSizeTabs();
38451     },
38452
38453     /**
38454      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
38455      */
38456     autoSizeTabs : function()
38457     {
38458         var count = this.items.length;
38459         var vcount = count - this.hiddenCount;
38460         
38461         if (vcount < 2) {
38462             this.stripEl.hide();
38463         } else {
38464             this.stripEl.show();
38465         }
38466         
38467         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
38468             return;
38469         }
38470         
38471         
38472         var w = Math.max(this.el.getWidth() - this.cpad, 10);
38473         var availWidth = Math.floor(w / vcount);
38474         var b = this.stripBody;
38475         if(b.getWidth() > w){
38476             var tabs = this.items;
38477             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
38478             if(availWidth < this.minTabWidth){
38479                 /*if(!this.sleft){    // incomplete scrolling code
38480                     this.createScrollButtons();
38481                 }
38482                 this.showScroll();
38483                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
38484             }
38485         }else{
38486             if(this.currentTabWidth < this.preferredTabWidth){
38487                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
38488             }
38489         }
38490     },
38491
38492     /**
38493      * Returns the number of tabs in this TabPanel.
38494      * @return {Number}
38495      */
38496      getCount : function(){
38497          return this.items.length;
38498      },
38499
38500     /**
38501      * Resizes all the tabs to the passed width
38502      * @param {Number} The new width
38503      */
38504     setTabWidth : function(width){
38505         this.currentTabWidth = width;
38506         for(var i = 0, len = this.items.length; i < len; i++) {
38507                 if(!this.items[i].isHidden()) {
38508                 this.items[i].setWidth(width);
38509             }
38510         }
38511     },
38512
38513     /**
38514      * Destroys this TabPanel
38515      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
38516      */
38517     destroy : function(removeEl){
38518         Roo.EventManager.removeResizeListener(this.onResize, this);
38519         for(var i = 0, len = this.items.length; i < len; i++){
38520             this.items[i].purgeListeners();
38521         }
38522         if(removeEl === true){
38523             this.el.update("");
38524             this.el.remove();
38525         }
38526     },
38527     
38528     createStrip : function(container)
38529     {
38530         var strip = document.createElement("nav");
38531         strip.className = Roo.bootstrap.version == 4 ?
38532             "navbar-light bg-light" : 
38533             "navbar navbar-default"; //"x-tabs-wrap";
38534         container.appendChild(strip);
38535         return strip;
38536     },
38537     
38538     createStripList : function(strip)
38539     {
38540         // div wrapper for retard IE
38541         // returns the "tr" element.
38542         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
38543         //'<div class="x-tabs-strip-wrap">'+
38544           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
38545           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
38546         return strip.firstChild; //.firstChild.firstChild.firstChild;
38547     },
38548     createBody : function(container)
38549     {
38550         var body = document.createElement("div");
38551         Roo.id(body, "tab-body");
38552         //Roo.fly(body).addClass("x-tabs-body");
38553         Roo.fly(body).addClass("tab-content");
38554         container.appendChild(body);
38555         return body;
38556     },
38557     createItemBody :function(bodyEl, id){
38558         var body = Roo.getDom(id);
38559         if(!body){
38560             body = document.createElement("div");
38561             body.id = id;
38562         }
38563         //Roo.fly(body).addClass("x-tabs-item-body");
38564         Roo.fly(body).addClass("tab-pane");
38565          bodyEl.insertBefore(body, bodyEl.firstChild);
38566         return body;
38567     },
38568     /** @private */
38569     createStripElements :  function(stripEl, text, closable, tpl)
38570     {
38571         var td = document.createElement("li"); // was td..
38572         td.className = 'nav-item';
38573         
38574         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
38575         
38576         
38577         stripEl.appendChild(td);
38578         /*if(closable){
38579             td.className = "x-tabs-closable";
38580             if(!this.closeTpl){
38581                 this.closeTpl = new Roo.Template(
38582                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38583                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
38584                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
38585                 );
38586             }
38587             var el = this.closeTpl.overwrite(td, {"text": text});
38588             var close = el.getElementsByTagName("div")[0];
38589             var inner = el.getElementsByTagName("em")[0];
38590             return {"el": el, "close": close, "inner": inner};
38591         } else {
38592         */
38593         // not sure what this is..
38594 //            if(!this.tabTpl){
38595                 //this.tabTpl = new Roo.Template(
38596                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
38597                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
38598                 //);
38599 //                this.tabTpl = new Roo.Template(
38600 //                   '<a href="#">' +
38601 //                   '<span unselectable="on"' +
38602 //                            (this.disableTooltips ? '' : ' title="{text}"') +
38603 //                            ' >{text}</span></a>'
38604 //                );
38605 //                
38606 //            }
38607
38608
38609             var template = tpl || this.tabTpl || false;
38610             
38611             if(!template){
38612                 template =  new Roo.Template(
38613                         Roo.bootstrap.version == 4 ? 
38614                             (
38615                                 '<a class="nav-link" href="#" unselectable="on"' +
38616                                      (this.disableTooltips ? '' : ' title="{text}"') +
38617                                      ' >{text}</a>'
38618                             ) : (
38619                                 '<a class="nav-link" href="#">' +
38620                                 '<span unselectable="on"' +
38621                                          (this.disableTooltips ? '' : ' title="{text}"') +
38622                                     ' >{text}</span></a>'
38623                             )
38624                 );
38625             }
38626             
38627             switch (typeof(template)) {
38628                 case 'object' :
38629                     break;
38630                 case 'string' :
38631                     template = new Roo.Template(template);
38632                     break;
38633                 default :
38634                     break;
38635             }
38636             
38637             var el = template.overwrite(td, {"text": text});
38638             
38639             var inner = el.getElementsByTagName("span")[0];
38640             
38641             return {"el": el, "inner": inner};
38642             
38643     }
38644         
38645     
38646 });
38647
38648 /**
38649  * @class Roo.TabPanelItem
38650  * @extends Roo.util.Observable
38651  * Represents an individual item (tab plus body) in a TabPanel.
38652  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
38653  * @param {String} id The id of this TabPanelItem
38654  * @param {String} text The text for the tab of this TabPanelItem
38655  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
38656  */
38657 Roo.bootstrap.panel.TabItem = function(config){
38658     /**
38659      * The {@link Roo.TabPanel} this TabPanelItem belongs to
38660      * @type Roo.TabPanel
38661      */
38662     this.tabPanel = config.panel;
38663     /**
38664      * The id for this TabPanelItem
38665      * @type String
38666      */
38667     this.id = config.id;
38668     /** @private */
38669     this.disabled = false;
38670     /** @private */
38671     this.text = config.text;
38672     /** @private */
38673     this.loaded = false;
38674     this.closable = config.closable;
38675
38676     /**
38677      * The body element for this TabPanelItem.
38678      * @type Roo.Element
38679      */
38680     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
38681     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
38682     this.bodyEl.setStyle("display", "block");
38683     this.bodyEl.setStyle("zoom", "1");
38684     //this.hideAction();
38685
38686     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
38687     /** @private */
38688     this.el = Roo.get(els.el);
38689     this.inner = Roo.get(els.inner, true);
38690      this.textEl = Roo.bootstrap.version == 4 ?
38691         this.el : Roo.get(this.el.dom.firstChild, true);
38692
38693     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
38694     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
38695
38696     
38697 //    this.el.on("mousedown", this.onTabMouseDown, this);
38698     this.el.on("click", this.onTabClick, this);
38699     /** @private */
38700     if(config.closable){
38701         var c = Roo.get(els.close, true);
38702         c.dom.title = this.closeText;
38703         c.addClassOnOver("close-over");
38704         c.on("click", this.closeClick, this);
38705      }
38706
38707     this.addEvents({
38708          /**
38709          * @event activate
38710          * Fires when this tab becomes the active tab.
38711          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38712          * @param {Roo.TabPanelItem} this
38713          */
38714         "activate": true,
38715         /**
38716          * @event beforeclose
38717          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
38718          * @param {Roo.TabPanelItem} this
38719          * @param {Object} e Set cancel to true on this object to cancel the close.
38720          */
38721         "beforeclose": true,
38722         /**
38723          * @event close
38724          * Fires when this tab is closed.
38725          * @param {Roo.TabPanelItem} this
38726          */
38727          "close": true,
38728         /**
38729          * @event deactivate
38730          * Fires when this tab is no longer the active tab.
38731          * @param {Roo.TabPanel} tabPanel The parent TabPanel
38732          * @param {Roo.TabPanelItem} this
38733          */
38734          "deactivate" : true
38735     });
38736     this.hidden = false;
38737
38738     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
38739 };
38740
38741 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
38742            {
38743     purgeListeners : function(){
38744        Roo.util.Observable.prototype.purgeListeners.call(this);
38745        this.el.removeAllListeners();
38746     },
38747     /**
38748      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
38749      */
38750     show : function(){
38751         this.status_node.addClass("active");
38752         this.showAction();
38753         if(Roo.isOpera){
38754             this.tabPanel.stripWrap.repaint();
38755         }
38756         this.fireEvent("activate", this.tabPanel, this);
38757     },
38758
38759     /**
38760      * Returns true if this tab is the active tab.
38761      * @return {Boolean}
38762      */
38763     isActive : function(){
38764         return this.tabPanel.getActiveTab() == this;
38765     },
38766
38767     /**
38768      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
38769      */
38770     hide : function(){
38771         this.status_node.removeClass("active");
38772         this.hideAction();
38773         this.fireEvent("deactivate", this.tabPanel, this);
38774     },
38775
38776     hideAction : function(){
38777         this.bodyEl.hide();
38778         this.bodyEl.setStyle("position", "absolute");
38779         this.bodyEl.setLeft("-20000px");
38780         this.bodyEl.setTop("-20000px");
38781     },
38782
38783     showAction : function(){
38784         this.bodyEl.setStyle("position", "relative");
38785         this.bodyEl.setTop("");
38786         this.bodyEl.setLeft("");
38787         this.bodyEl.show();
38788     },
38789
38790     /**
38791      * Set the tooltip for the tab.
38792      * @param {String} tooltip The tab's tooltip
38793      */
38794     setTooltip : function(text){
38795         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38796             this.textEl.dom.qtip = text;
38797             this.textEl.dom.removeAttribute('title');
38798         }else{
38799             this.textEl.dom.title = text;
38800         }
38801     },
38802
38803     onTabClick : function(e){
38804         e.preventDefault();
38805         this.tabPanel.activate(this.id);
38806     },
38807
38808     onTabMouseDown : function(e){
38809         e.preventDefault();
38810         this.tabPanel.activate(this.id);
38811     },
38812 /*
38813     getWidth : function(){
38814         return this.inner.getWidth();
38815     },
38816
38817     setWidth : function(width){
38818         var iwidth = width - this.linode.getPadding("lr");
38819         this.inner.setWidth(iwidth);
38820         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38821         this.linode.setWidth(width);
38822     },
38823 */
38824     /**
38825      * Show or hide the tab
38826      * @param {Boolean} hidden True to hide or false to show.
38827      */
38828     setHidden : function(hidden){
38829         this.hidden = hidden;
38830         this.linode.setStyle("display", hidden ? "none" : "");
38831     },
38832
38833     /**
38834      * Returns true if this tab is "hidden"
38835      * @return {Boolean}
38836      */
38837     isHidden : function(){
38838         return this.hidden;
38839     },
38840
38841     /**
38842      * Returns the text for this tab
38843      * @return {String}
38844      */
38845     getText : function(){
38846         return this.text;
38847     },
38848     /*
38849     autoSize : function(){
38850         //this.el.beginMeasure();
38851         this.textEl.setWidth(1);
38852         /*
38853          *  #2804 [new] Tabs in Roojs
38854          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38855          */
38856         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38857         //this.el.endMeasure();
38858     //},
38859
38860     /**
38861      * Sets the text for the tab (Note: this also sets the tooltip text)
38862      * @param {String} text The tab's text and tooltip
38863      */
38864     setText : function(text){
38865         this.text = text;
38866         this.textEl.update(text);
38867         this.setTooltip(text);
38868         //if(!this.tabPanel.resizeTabs){
38869         //    this.autoSize();
38870         //}
38871     },
38872     /**
38873      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38874      */
38875     activate : function(){
38876         this.tabPanel.activate(this.id);
38877     },
38878
38879     /**
38880      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38881      */
38882     disable : function(){
38883         if(this.tabPanel.active != this){
38884             this.disabled = true;
38885             this.status_node.addClass("disabled");
38886         }
38887     },
38888
38889     /**
38890      * Enables this TabPanelItem if it was previously disabled.
38891      */
38892     enable : function(){
38893         this.disabled = false;
38894         this.status_node.removeClass("disabled");
38895     },
38896
38897     /**
38898      * Sets the content for this TabPanelItem.
38899      * @param {String} content The content
38900      * @param {Boolean} loadScripts true to look for and load scripts
38901      */
38902     setContent : function(content, loadScripts){
38903         this.bodyEl.update(content, loadScripts);
38904     },
38905
38906     /**
38907      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38908      * @return {Roo.UpdateManager} The UpdateManager
38909      */
38910     getUpdateManager : function(){
38911         return this.bodyEl.getUpdateManager();
38912     },
38913
38914     /**
38915      * Set a URL to be used to load the content for this TabPanelItem.
38916      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38917      * @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)
38918      * @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)
38919      * @return {Roo.UpdateManager} The UpdateManager
38920      */
38921     setUrl : function(url, params, loadOnce){
38922         if(this.refreshDelegate){
38923             this.un('activate', this.refreshDelegate);
38924         }
38925         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38926         this.on("activate", this.refreshDelegate);
38927         return this.bodyEl.getUpdateManager();
38928     },
38929
38930     /** @private */
38931     _handleRefresh : function(url, params, loadOnce){
38932         if(!loadOnce || !this.loaded){
38933             var updater = this.bodyEl.getUpdateManager();
38934             updater.update(url, params, this._setLoaded.createDelegate(this));
38935         }
38936     },
38937
38938     /**
38939      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38940      *   Will fail silently if the setUrl method has not been called.
38941      *   This does not activate the panel, just updates its content.
38942      */
38943     refresh : function(){
38944         if(this.refreshDelegate){
38945            this.loaded = false;
38946            this.refreshDelegate();
38947         }
38948     },
38949
38950     /** @private */
38951     _setLoaded : function(){
38952         this.loaded = true;
38953     },
38954
38955     /** @private */
38956     closeClick : function(e){
38957         var o = {};
38958         e.stopEvent();
38959         this.fireEvent("beforeclose", this, o);
38960         if(o.cancel !== true){
38961             this.tabPanel.removeTab(this.id);
38962         }
38963     },
38964     /**
38965      * The text displayed in the tooltip for the close icon.
38966      * @type String
38967      */
38968     closeText : "Close this tab"
38969 });
38970 /**
38971 *    This script refer to:
38972 *    Title: International Telephone Input
38973 *    Author: Jack O'Connor
38974 *    Code version:  v12.1.12
38975 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38976 **/
38977
38978 Roo.bootstrap.PhoneInputData = function() {
38979     var d = [
38980       [
38981         "Afghanistan (‫افغانستان‬‎)",
38982         "af",
38983         "93"
38984       ],
38985       [
38986         "Albania (Shqipëri)",
38987         "al",
38988         "355"
38989       ],
38990       [
38991         "Algeria (‫الجزائر‬‎)",
38992         "dz",
38993         "213"
38994       ],
38995       [
38996         "American Samoa",
38997         "as",
38998         "1684"
38999       ],
39000       [
39001         "Andorra",
39002         "ad",
39003         "376"
39004       ],
39005       [
39006         "Angola",
39007         "ao",
39008         "244"
39009       ],
39010       [
39011         "Anguilla",
39012         "ai",
39013         "1264"
39014       ],
39015       [
39016         "Antigua and Barbuda",
39017         "ag",
39018         "1268"
39019       ],
39020       [
39021         "Argentina",
39022         "ar",
39023         "54"
39024       ],
39025       [
39026         "Armenia (Հայաստան)",
39027         "am",
39028         "374"
39029       ],
39030       [
39031         "Aruba",
39032         "aw",
39033         "297"
39034       ],
39035       [
39036         "Australia",
39037         "au",
39038         "61",
39039         0
39040       ],
39041       [
39042         "Austria (Österreich)",
39043         "at",
39044         "43"
39045       ],
39046       [
39047         "Azerbaijan (Azərbaycan)",
39048         "az",
39049         "994"
39050       ],
39051       [
39052         "Bahamas",
39053         "bs",
39054         "1242"
39055       ],
39056       [
39057         "Bahrain (‫البحرين‬‎)",
39058         "bh",
39059         "973"
39060       ],
39061       [
39062         "Bangladesh (বাংলাদেশ)",
39063         "bd",
39064         "880"
39065       ],
39066       [
39067         "Barbados",
39068         "bb",
39069         "1246"
39070       ],
39071       [
39072         "Belarus (Беларусь)",
39073         "by",
39074         "375"
39075       ],
39076       [
39077         "Belgium (België)",
39078         "be",
39079         "32"
39080       ],
39081       [
39082         "Belize",
39083         "bz",
39084         "501"
39085       ],
39086       [
39087         "Benin (Bénin)",
39088         "bj",
39089         "229"
39090       ],
39091       [
39092         "Bermuda",
39093         "bm",
39094         "1441"
39095       ],
39096       [
39097         "Bhutan (འབྲུག)",
39098         "bt",
39099         "975"
39100       ],
39101       [
39102         "Bolivia",
39103         "bo",
39104         "591"
39105       ],
39106       [
39107         "Bosnia and Herzegovina (Босна и Херцеговина)",
39108         "ba",
39109         "387"
39110       ],
39111       [
39112         "Botswana",
39113         "bw",
39114         "267"
39115       ],
39116       [
39117         "Brazil (Brasil)",
39118         "br",
39119         "55"
39120       ],
39121       [
39122         "British Indian Ocean Territory",
39123         "io",
39124         "246"
39125       ],
39126       [
39127         "British Virgin Islands",
39128         "vg",
39129         "1284"
39130       ],
39131       [
39132         "Brunei",
39133         "bn",
39134         "673"
39135       ],
39136       [
39137         "Bulgaria (България)",
39138         "bg",
39139         "359"
39140       ],
39141       [
39142         "Burkina Faso",
39143         "bf",
39144         "226"
39145       ],
39146       [
39147         "Burundi (Uburundi)",
39148         "bi",
39149         "257"
39150       ],
39151       [
39152         "Cambodia (កម្ពុជា)",
39153         "kh",
39154         "855"
39155       ],
39156       [
39157         "Cameroon (Cameroun)",
39158         "cm",
39159         "237"
39160       ],
39161       [
39162         "Canada",
39163         "ca",
39164         "1",
39165         1,
39166         ["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"]
39167       ],
39168       [
39169         "Cape Verde (Kabu Verdi)",
39170         "cv",
39171         "238"
39172       ],
39173       [
39174         "Caribbean Netherlands",
39175         "bq",
39176         "599",
39177         1
39178       ],
39179       [
39180         "Cayman Islands",
39181         "ky",
39182         "1345"
39183       ],
39184       [
39185         "Central African Republic (République centrafricaine)",
39186         "cf",
39187         "236"
39188       ],
39189       [
39190         "Chad (Tchad)",
39191         "td",
39192         "235"
39193       ],
39194       [
39195         "Chile",
39196         "cl",
39197         "56"
39198       ],
39199       [
39200         "China (中国)",
39201         "cn",
39202         "86"
39203       ],
39204       [
39205         "Christmas Island",
39206         "cx",
39207         "61",
39208         2
39209       ],
39210       [
39211         "Cocos (Keeling) Islands",
39212         "cc",
39213         "61",
39214         1
39215       ],
39216       [
39217         "Colombia",
39218         "co",
39219         "57"
39220       ],
39221       [
39222         "Comoros (‫جزر القمر‬‎)",
39223         "km",
39224         "269"
39225       ],
39226       [
39227         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
39228         "cd",
39229         "243"
39230       ],
39231       [
39232         "Congo (Republic) (Congo-Brazzaville)",
39233         "cg",
39234         "242"
39235       ],
39236       [
39237         "Cook Islands",
39238         "ck",
39239         "682"
39240       ],
39241       [
39242         "Costa Rica",
39243         "cr",
39244         "506"
39245       ],
39246       [
39247         "Côte d’Ivoire",
39248         "ci",
39249         "225"
39250       ],
39251       [
39252         "Croatia (Hrvatska)",
39253         "hr",
39254         "385"
39255       ],
39256       [
39257         "Cuba",
39258         "cu",
39259         "53"
39260       ],
39261       [
39262         "Curaçao",
39263         "cw",
39264         "599",
39265         0
39266       ],
39267       [
39268         "Cyprus (Κύπρος)",
39269         "cy",
39270         "357"
39271       ],
39272       [
39273         "Czech Republic (Česká republika)",
39274         "cz",
39275         "420"
39276       ],
39277       [
39278         "Denmark (Danmark)",
39279         "dk",
39280         "45"
39281       ],
39282       [
39283         "Djibouti",
39284         "dj",
39285         "253"
39286       ],
39287       [
39288         "Dominica",
39289         "dm",
39290         "1767"
39291       ],
39292       [
39293         "Dominican Republic (República Dominicana)",
39294         "do",
39295         "1",
39296         2,
39297         ["809", "829", "849"]
39298       ],
39299       [
39300         "Ecuador",
39301         "ec",
39302         "593"
39303       ],
39304       [
39305         "Egypt (‫مصر‬‎)",
39306         "eg",
39307         "20"
39308       ],
39309       [
39310         "El Salvador",
39311         "sv",
39312         "503"
39313       ],
39314       [
39315         "Equatorial Guinea (Guinea Ecuatorial)",
39316         "gq",
39317         "240"
39318       ],
39319       [
39320         "Eritrea",
39321         "er",
39322         "291"
39323       ],
39324       [
39325         "Estonia (Eesti)",
39326         "ee",
39327         "372"
39328       ],
39329       [
39330         "Ethiopia",
39331         "et",
39332         "251"
39333       ],
39334       [
39335         "Falkland Islands (Islas Malvinas)",
39336         "fk",
39337         "500"
39338       ],
39339       [
39340         "Faroe Islands (Føroyar)",
39341         "fo",
39342         "298"
39343       ],
39344       [
39345         "Fiji",
39346         "fj",
39347         "679"
39348       ],
39349       [
39350         "Finland (Suomi)",
39351         "fi",
39352         "358",
39353         0
39354       ],
39355       [
39356         "France",
39357         "fr",
39358         "33"
39359       ],
39360       [
39361         "French Guiana (Guyane française)",
39362         "gf",
39363         "594"
39364       ],
39365       [
39366         "French Polynesia (Polynésie française)",
39367         "pf",
39368         "689"
39369       ],
39370       [
39371         "Gabon",
39372         "ga",
39373         "241"
39374       ],
39375       [
39376         "Gambia",
39377         "gm",
39378         "220"
39379       ],
39380       [
39381         "Georgia (საქართველო)",
39382         "ge",
39383         "995"
39384       ],
39385       [
39386         "Germany (Deutschland)",
39387         "de",
39388         "49"
39389       ],
39390       [
39391         "Ghana (Gaana)",
39392         "gh",
39393         "233"
39394       ],
39395       [
39396         "Gibraltar",
39397         "gi",
39398         "350"
39399       ],
39400       [
39401         "Greece (Ελλάδα)",
39402         "gr",
39403         "30"
39404       ],
39405       [
39406         "Greenland (Kalaallit Nunaat)",
39407         "gl",
39408         "299"
39409       ],
39410       [
39411         "Grenada",
39412         "gd",
39413         "1473"
39414       ],
39415       [
39416         "Guadeloupe",
39417         "gp",
39418         "590",
39419         0
39420       ],
39421       [
39422         "Guam",
39423         "gu",
39424         "1671"
39425       ],
39426       [
39427         "Guatemala",
39428         "gt",
39429         "502"
39430       ],
39431       [
39432         "Guernsey",
39433         "gg",
39434         "44",
39435         1
39436       ],
39437       [
39438         "Guinea (Guinée)",
39439         "gn",
39440         "224"
39441       ],
39442       [
39443         "Guinea-Bissau (Guiné Bissau)",
39444         "gw",
39445         "245"
39446       ],
39447       [
39448         "Guyana",
39449         "gy",
39450         "592"
39451       ],
39452       [
39453         "Haiti",
39454         "ht",
39455         "509"
39456       ],
39457       [
39458         "Honduras",
39459         "hn",
39460         "504"
39461       ],
39462       [
39463         "Hong Kong (香港)",
39464         "hk",
39465         "852"
39466       ],
39467       [
39468         "Hungary (Magyarország)",
39469         "hu",
39470         "36"
39471       ],
39472       [
39473         "Iceland (Ísland)",
39474         "is",
39475         "354"
39476       ],
39477       [
39478         "India (भारत)",
39479         "in",
39480         "91"
39481       ],
39482       [
39483         "Indonesia",
39484         "id",
39485         "62"
39486       ],
39487       [
39488         "Iran (‫ایران‬‎)",
39489         "ir",
39490         "98"
39491       ],
39492       [
39493         "Iraq (‫العراق‬‎)",
39494         "iq",
39495         "964"
39496       ],
39497       [
39498         "Ireland",
39499         "ie",
39500         "353"
39501       ],
39502       [
39503         "Isle of Man",
39504         "im",
39505         "44",
39506         2
39507       ],
39508       [
39509         "Israel (‫ישראל‬‎)",
39510         "il",
39511         "972"
39512       ],
39513       [
39514         "Italy (Italia)",
39515         "it",
39516         "39",
39517         0
39518       ],
39519       [
39520         "Jamaica",
39521         "jm",
39522         "1876"
39523       ],
39524       [
39525         "Japan (日本)",
39526         "jp",
39527         "81"
39528       ],
39529       [
39530         "Jersey",
39531         "je",
39532         "44",
39533         3
39534       ],
39535       [
39536         "Jordan (‫الأردن‬‎)",
39537         "jo",
39538         "962"
39539       ],
39540       [
39541         "Kazakhstan (Казахстан)",
39542         "kz",
39543         "7",
39544         1
39545       ],
39546       [
39547         "Kenya",
39548         "ke",
39549         "254"
39550       ],
39551       [
39552         "Kiribati",
39553         "ki",
39554         "686"
39555       ],
39556       [
39557         "Kosovo",
39558         "xk",
39559         "383"
39560       ],
39561       [
39562         "Kuwait (‫الكويت‬‎)",
39563         "kw",
39564         "965"
39565       ],
39566       [
39567         "Kyrgyzstan (Кыргызстан)",
39568         "kg",
39569         "996"
39570       ],
39571       [
39572         "Laos (ລາວ)",
39573         "la",
39574         "856"
39575       ],
39576       [
39577         "Latvia (Latvija)",
39578         "lv",
39579         "371"
39580       ],
39581       [
39582         "Lebanon (‫لبنان‬‎)",
39583         "lb",
39584         "961"
39585       ],
39586       [
39587         "Lesotho",
39588         "ls",
39589         "266"
39590       ],
39591       [
39592         "Liberia",
39593         "lr",
39594         "231"
39595       ],
39596       [
39597         "Libya (‫ليبيا‬‎)",
39598         "ly",
39599         "218"
39600       ],
39601       [
39602         "Liechtenstein",
39603         "li",
39604         "423"
39605       ],
39606       [
39607         "Lithuania (Lietuva)",
39608         "lt",
39609         "370"
39610       ],
39611       [
39612         "Luxembourg",
39613         "lu",
39614         "352"
39615       ],
39616       [
39617         "Macau (澳門)",
39618         "mo",
39619         "853"
39620       ],
39621       [
39622         "Macedonia (FYROM) (Македонија)",
39623         "mk",
39624         "389"
39625       ],
39626       [
39627         "Madagascar (Madagasikara)",
39628         "mg",
39629         "261"
39630       ],
39631       [
39632         "Malawi",
39633         "mw",
39634         "265"
39635       ],
39636       [
39637         "Malaysia",
39638         "my",
39639         "60"
39640       ],
39641       [
39642         "Maldives",
39643         "mv",
39644         "960"
39645       ],
39646       [
39647         "Mali",
39648         "ml",
39649         "223"
39650       ],
39651       [
39652         "Malta",
39653         "mt",
39654         "356"
39655       ],
39656       [
39657         "Marshall Islands",
39658         "mh",
39659         "692"
39660       ],
39661       [
39662         "Martinique",
39663         "mq",
39664         "596"
39665       ],
39666       [
39667         "Mauritania (‫موريتانيا‬‎)",
39668         "mr",
39669         "222"
39670       ],
39671       [
39672         "Mauritius (Moris)",
39673         "mu",
39674         "230"
39675       ],
39676       [
39677         "Mayotte",
39678         "yt",
39679         "262",
39680         1
39681       ],
39682       [
39683         "Mexico (México)",
39684         "mx",
39685         "52"
39686       ],
39687       [
39688         "Micronesia",
39689         "fm",
39690         "691"
39691       ],
39692       [
39693         "Moldova (Republica Moldova)",
39694         "md",
39695         "373"
39696       ],
39697       [
39698         "Monaco",
39699         "mc",
39700         "377"
39701       ],
39702       [
39703         "Mongolia (Монгол)",
39704         "mn",
39705         "976"
39706       ],
39707       [
39708         "Montenegro (Crna Gora)",
39709         "me",
39710         "382"
39711       ],
39712       [
39713         "Montserrat",
39714         "ms",
39715         "1664"
39716       ],
39717       [
39718         "Morocco (‫المغرب‬‎)",
39719         "ma",
39720         "212",
39721         0
39722       ],
39723       [
39724         "Mozambique (Moçambique)",
39725         "mz",
39726         "258"
39727       ],
39728       [
39729         "Myanmar (Burma) (မြန်မာ)",
39730         "mm",
39731         "95"
39732       ],
39733       [
39734         "Namibia (Namibië)",
39735         "na",
39736         "264"
39737       ],
39738       [
39739         "Nauru",
39740         "nr",
39741         "674"
39742       ],
39743       [
39744         "Nepal (नेपाल)",
39745         "np",
39746         "977"
39747       ],
39748       [
39749         "Netherlands (Nederland)",
39750         "nl",
39751         "31"
39752       ],
39753       [
39754         "New Caledonia (Nouvelle-Calédonie)",
39755         "nc",
39756         "687"
39757       ],
39758       [
39759         "New Zealand",
39760         "nz",
39761         "64"
39762       ],
39763       [
39764         "Nicaragua",
39765         "ni",
39766         "505"
39767       ],
39768       [
39769         "Niger (Nijar)",
39770         "ne",
39771         "227"
39772       ],
39773       [
39774         "Nigeria",
39775         "ng",
39776         "234"
39777       ],
39778       [
39779         "Niue",
39780         "nu",
39781         "683"
39782       ],
39783       [
39784         "Norfolk Island",
39785         "nf",
39786         "672"
39787       ],
39788       [
39789         "North Korea (조선 민주주의 인민 공화국)",
39790         "kp",
39791         "850"
39792       ],
39793       [
39794         "Northern Mariana Islands",
39795         "mp",
39796         "1670"
39797       ],
39798       [
39799         "Norway (Norge)",
39800         "no",
39801         "47",
39802         0
39803       ],
39804       [
39805         "Oman (‫عُمان‬‎)",
39806         "om",
39807         "968"
39808       ],
39809       [
39810         "Pakistan (‫پاکستان‬‎)",
39811         "pk",
39812         "92"
39813       ],
39814       [
39815         "Palau",
39816         "pw",
39817         "680"
39818       ],
39819       [
39820         "Palestine (‫فلسطين‬‎)",
39821         "ps",
39822         "970"
39823       ],
39824       [
39825         "Panama (Panamá)",
39826         "pa",
39827         "507"
39828       ],
39829       [
39830         "Papua New Guinea",
39831         "pg",
39832         "675"
39833       ],
39834       [
39835         "Paraguay",
39836         "py",
39837         "595"
39838       ],
39839       [
39840         "Peru (Perú)",
39841         "pe",
39842         "51"
39843       ],
39844       [
39845         "Philippines",
39846         "ph",
39847         "63"
39848       ],
39849       [
39850         "Poland (Polska)",
39851         "pl",
39852         "48"
39853       ],
39854       [
39855         "Portugal",
39856         "pt",
39857         "351"
39858       ],
39859       [
39860         "Puerto Rico",
39861         "pr",
39862         "1",
39863         3,
39864         ["787", "939"]
39865       ],
39866       [
39867         "Qatar (‫قطر‬‎)",
39868         "qa",
39869         "974"
39870       ],
39871       [
39872         "Réunion (La Réunion)",
39873         "re",
39874         "262",
39875         0
39876       ],
39877       [
39878         "Romania (România)",
39879         "ro",
39880         "40"
39881       ],
39882       [
39883         "Russia (Россия)",
39884         "ru",
39885         "7",
39886         0
39887       ],
39888       [
39889         "Rwanda",
39890         "rw",
39891         "250"
39892       ],
39893       [
39894         "Saint Barthélemy",
39895         "bl",
39896         "590",
39897         1
39898       ],
39899       [
39900         "Saint Helena",
39901         "sh",
39902         "290"
39903       ],
39904       [
39905         "Saint Kitts and Nevis",
39906         "kn",
39907         "1869"
39908       ],
39909       [
39910         "Saint Lucia",
39911         "lc",
39912         "1758"
39913       ],
39914       [
39915         "Saint Martin (Saint-Martin (partie française))",
39916         "mf",
39917         "590",
39918         2
39919       ],
39920       [
39921         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39922         "pm",
39923         "508"
39924       ],
39925       [
39926         "Saint Vincent and the Grenadines",
39927         "vc",
39928         "1784"
39929       ],
39930       [
39931         "Samoa",
39932         "ws",
39933         "685"
39934       ],
39935       [
39936         "San Marino",
39937         "sm",
39938         "378"
39939       ],
39940       [
39941         "São Tomé and Príncipe (São Tomé e Príncipe)",
39942         "st",
39943         "239"
39944       ],
39945       [
39946         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39947         "sa",
39948         "966"
39949       ],
39950       [
39951         "Senegal (Sénégal)",
39952         "sn",
39953         "221"
39954       ],
39955       [
39956         "Serbia (Србија)",
39957         "rs",
39958         "381"
39959       ],
39960       [
39961         "Seychelles",
39962         "sc",
39963         "248"
39964       ],
39965       [
39966         "Sierra Leone",
39967         "sl",
39968         "232"
39969       ],
39970       [
39971         "Singapore",
39972         "sg",
39973         "65"
39974       ],
39975       [
39976         "Sint Maarten",
39977         "sx",
39978         "1721"
39979       ],
39980       [
39981         "Slovakia (Slovensko)",
39982         "sk",
39983         "421"
39984       ],
39985       [
39986         "Slovenia (Slovenija)",
39987         "si",
39988         "386"
39989       ],
39990       [
39991         "Solomon Islands",
39992         "sb",
39993         "677"
39994       ],
39995       [
39996         "Somalia (Soomaaliya)",
39997         "so",
39998         "252"
39999       ],
40000       [
40001         "South Africa",
40002         "za",
40003         "27"
40004       ],
40005       [
40006         "South Korea (대한민국)",
40007         "kr",
40008         "82"
40009       ],
40010       [
40011         "South Sudan (‫جنوب السودان‬‎)",
40012         "ss",
40013         "211"
40014       ],
40015       [
40016         "Spain (España)",
40017         "es",
40018         "34"
40019       ],
40020       [
40021         "Sri Lanka (ශ්‍රී ලංකාව)",
40022         "lk",
40023         "94"
40024       ],
40025       [
40026         "Sudan (‫السودان‬‎)",
40027         "sd",
40028         "249"
40029       ],
40030       [
40031         "Suriname",
40032         "sr",
40033         "597"
40034       ],
40035       [
40036         "Svalbard and Jan Mayen",
40037         "sj",
40038         "47",
40039         1
40040       ],
40041       [
40042         "Swaziland",
40043         "sz",
40044         "268"
40045       ],
40046       [
40047         "Sweden (Sverige)",
40048         "se",
40049         "46"
40050       ],
40051       [
40052         "Switzerland (Schweiz)",
40053         "ch",
40054         "41"
40055       ],
40056       [
40057         "Syria (‫سوريا‬‎)",
40058         "sy",
40059         "963"
40060       ],
40061       [
40062         "Taiwan (台灣)",
40063         "tw",
40064         "886"
40065       ],
40066       [
40067         "Tajikistan",
40068         "tj",
40069         "992"
40070       ],
40071       [
40072         "Tanzania",
40073         "tz",
40074         "255"
40075       ],
40076       [
40077         "Thailand (ไทย)",
40078         "th",
40079         "66"
40080       ],
40081       [
40082         "Timor-Leste",
40083         "tl",
40084         "670"
40085       ],
40086       [
40087         "Togo",
40088         "tg",
40089         "228"
40090       ],
40091       [
40092         "Tokelau",
40093         "tk",
40094         "690"
40095       ],
40096       [
40097         "Tonga",
40098         "to",
40099         "676"
40100       ],
40101       [
40102         "Trinidad and Tobago",
40103         "tt",
40104         "1868"
40105       ],
40106       [
40107         "Tunisia (‫تونس‬‎)",
40108         "tn",
40109         "216"
40110       ],
40111       [
40112         "Turkey (Türkiye)",
40113         "tr",
40114         "90"
40115       ],
40116       [
40117         "Turkmenistan",
40118         "tm",
40119         "993"
40120       ],
40121       [
40122         "Turks and Caicos Islands",
40123         "tc",
40124         "1649"
40125       ],
40126       [
40127         "Tuvalu",
40128         "tv",
40129         "688"
40130       ],
40131       [
40132         "U.S. Virgin Islands",
40133         "vi",
40134         "1340"
40135       ],
40136       [
40137         "Uganda",
40138         "ug",
40139         "256"
40140       ],
40141       [
40142         "Ukraine (Україна)",
40143         "ua",
40144         "380"
40145       ],
40146       [
40147         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
40148         "ae",
40149         "971"
40150       ],
40151       [
40152         "United Kingdom",
40153         "gb",
40154         "44",
40155         0
40156       ],
40157       [
40158         "United States",
40159         "us",
40160         "1",
40161         0
40162       ],
40163       [
40164         "Uruguay",
40165         "uy",
40166         "598"
40167       ],
40168       [
40169         "Uzbekistan (Oʻzbekiston)",
40170         "uz",
40171         "998"
40172       ],
40173       [
40174         "Vanuatu",
40175         "vu",
40176         "678"
40177       ],
40178       [
40179         "Vatican City (Città del Vaticano)",
40180         "va",
40181         "39",
40182         1
40183       ],
40184       [
40185         "Venezuela",
40186         "ve",
40187         "58"
40188       ],
40189       [
40190         "Vietnam (Việt Nam)",
40191         "vn",
40192         "84"
40193       ],
40194       [
40195         "Wallis and Futuna (Wallis-et-Futuna)",
40196         "wf",
40197         "681"
40198       ],
40199       [
40200         "Western Sahara (‫الصحراء الغربية‬‎)",
40201         "eh",
40202         "212",
40203         1
40204       ],
40205       [
40206         "Yemen (‫اليمن‬‎)",
40207         "ye",
40208         "967"
40209       ],
40210       [
40211         "Zambia",
40212         "zm",
40213         "260"
40214       ],
40215       [
40216         "Zimbabwe",
40217         "zw",
40218         "263"
40219       ],
40220       [
40221         "Åland Islands",
40222         "ax",
40223         "358",
40224         1
40225       ]
40226   ];
40227   
40228   return d;
40229 }/**
40230 *    This script refer to:
40231 *    Title: International Telephone Input
40232 *    Author: Jack O'Connor
40233 *    Code version:  v12.1.12
40234 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40235 **/
40236
40237 /**
40238  * @class Roo.bootstrap.PhoneInput
40239  * @extends Roo.bootstrap.TriggerField
40240  * An input with International dial-code selection
40241  
40242  * @cfg {String} defaultDialCode default '+852'
40243  * @cfg {Array} preferedCountries default []
40244   
40245  * @constructor
40246  * Create a new PhoneInput.
40247  * @param {Object} config Configuration options
40248  */
40249
40250 Roo.bootstrap.PhoneInput = function(config) {
40251     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
40252 };
40253
40254 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
40255         
40256         listWidth: undefined,
40257         
40258         selectedClass: 'active',
40259         
40260         invalidClass : "has-warning",
40261         
40262         validClass: 'has-success',
40263         
40264         allowed: '0123456789',
40265         
40266         max_length: 15,
40267         
40268         /**
40269          * @cfg {String} defaultDialCode The default dial code when initializing the input
40270          */
40271         defaultDialCode: '+852',
40272         
40273         /**
40274          * @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
40275          */
40276         preferedCountries: false,
40277         
40278         getAutoCreate : function()
40279         {
40280             var data = Roo.bootstrap.PhoneInputData();
40281             var align = this.labelAlign || this.parentLabelAlign();
40282             var id = Roo.id();
40283             
40284             this.allCountries = [];
40285             this.dialCodeMapping = [];
40286             
40287             for (var i = 0; i < data.length; i++) {
40288               var c = data[i];
40289               this.allCountries[i] = {
40290                 name: c[0],
40291                 iso2: c[1],
40292                 dialCode: c[2],
40293                 priority: c[3] || 0,
40294                 areaCodes: c[4] || null
40295               };
40296               this.dialCodeMapping[c[2]] = {
40297                   name: c[0],
40298                   iso2: c[1],
40299                   priority: c[3] || 0,
40300                   areaCodes: c[4] || null
40301               };
40302             }
40303             
40304             var cfg = {
40305                 cls: 'form-group',
40306                 cn: []
40307             };
40308             
40309             var input =  {
40310                 tag: 'input',
40311                 id : id,
40312                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
40313                 maxlength: this.max_length,
40314                 cls : 'form-control tel-input',
40315                 autocomplete: 'new-password'
40316             };
40317             
40318             var hiddenInput = {
40319                 tag: 'input',
40320                 type: 'hidden',
40321                 cls: 'hidden-tel-input'
40322             };
40323             
40324             if (this.name) {
40325                 hiddenInput.name = this.name;
40326             }
40327             
40328             if (this.disabled) {
40329                 input.disabled = true;
40330             }
40331             
40332             var flag_container = {
40333                 tag: 'div',
40334                 cls: 'flag-box',
40335                 cn: [
40336                     {
40337                         tag: 'div',
40338                         cls: 'flag'
40339                     },
40340                     {
40341                         tag: 'div',
40342                         cls: 'caret'
40343                     }
40344                 ]
40345             };
40346             
40347             var box = {
40348                 tag: 'div',
40349                 cls: this.hasFeedback ? 'has-feedback' : '',
40350                 cn: [
40351                     hiddenInput,
40352                     input,
40353                     {
40354                         tag: 'input',
40355                         cls: 'dial-code-holder',
40356                         disabled: true
40357                     }
40358                 ]
40359             };
40360             
40361             var container = {
40362                 cls: 'roo-select2-container input-group',
40363                 cn: [
40364                     flag_container,
40365                     box
40366                 ]
40367             };
40368             
40369             if (this.fieldLabel.length) {
40370                 var indicator = {
40371                     tag: 'i',
40372                     tooltip: 'This field is required'
40373                 };
40374                 
40375                 var label = {
40376                     tag: 'label',
40377                     'for':  id,
40378                     cls: 'control-label',
40379                     cn: []
40380                 };
40381                 
40382                 var label_text = {
40383                     tag: 'span',
40384                     html: this.fieldLabel
40385                 };
40386                 
40387                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40388                 label.cn = [
40389                     indicator,
40390                     label_text
40391                 ];
40392                 
40393                 if(this.indicatorpos == 'right') {
40394                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40395                     label.cn = [
40396                         label_text,
40397                         indicator
40398                     ];
40399                 }
40400                 
40401                 if(align == 'left') {
40402                     container = {
40403                         tag: 'div',
40404                         cn: [
40405                             container
40406                         ]
40407                     };
40408                     
40409                     if(this.labelWidth > 12){
40410                         label.style = "width: " + this.labelWidth + 'px';
40411                     }
40412                     if(this.labelWidth < 13 && this.labelmd == 0){
40413                         this.labelmd = this.labelWidth;
40414                     }
40415                     if(this.labellg > 0){
40416                         label.cls += ' col-lg-' + this.labellg;
40417                         input.cls += ' col-lg-' + (12 - this.labellg);
40418                     }
40419                     if(this.labelmd > 0){
40420                         label.cls += ' col-md-' + this.labelmd;
40421                         container.cls += ' col-md-' + (12 - this.labelmd);
40422                     }
40423                     if(this.labelsm > 0){
40424                         label.cls += ' col-sm-' + this.labelsm;
40425                         container.cls += ' col-sm-' + (12 - this.labelsm);
40426                     }
40427                     if(this.labelxs > 0){
40428                         label.cls += ' col-xs-' + this.labelxs;
40429                         container.cls += ' col-xs-' + (12 - this.labelxs);
40430                     }
40431                 }
40432             }
40433             
40434             cfg.cn = [
40435                 label,
40436                 container
40437             ];
40438             
40439             var settings = this;
40440             
40441             ['xs','sm','md','lg'].map(function(size){
40442                 if (settings[size]) {
40443                     cfg.cls += ' col-' + size + '-' + settings[size];
40444                 }
40445             });
40446             
40447             this.store = new Roo.data.Store({
40448                 proxy : new Roo.data.MemoryProxy({}),
40449                 reader : new Roo.data.JsonReader({
40450                     fields : [
40451                         {
40452                             'name' : 'name',
40453                             'type' : 'string'
40454                         },
40455                         {
40456                             'name' : 'iso2',
40457                             'type' : 'string'
40458                         },
40459                         {
40460                             'name' : 'dialCode',
40461                             'type' : 'string'
40462                         },
40463                         {
40464                             'name' : 'priority',
40465                             'type' : 'string'
40466                         },
40467                         {
40468                             'name' : 'areaCodes',
40469                             'type' : 'string'
40470                         }
40471                     ]
40472                 })
40473             });
40474             
40475             if(!this.preferedCountries) {
40476                 this.preferedCountries = [
40477                     'hk',
40478                     'gb',
40479                     'us'
40480                 ];
40481             }
40482             
40483             var p = this.preferedCountries.reverse();
40484             
40485             if(p) {
40486                 for (var i = 0; i < p.length; i++) {
40487                     for (var j = 0; j < this.allCountries.length; j++) {
40488                         if(this.allCountries[j].iso2 == p[i]) {
40489                             var t = this.allCountries[j];
40490                             this.allCountries.splice(j,1);
40491                             this.allCountries.unshift(t);
40492                         }
40493                     } 
40494                 }
40495             }
40496             
40497             this.store.proxy.data = {
40498                 success: true,
40499                 data: this.allCountries
40500             };
40501             
40502             return cfg;
40503         },
40504         
40505         initEvents : function()
40506         {
40507             this.createList();
40508             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
40509             
40510             this.indicator = this.indicatorEl();
40511             this.flag = this.flagEl();
40512             this.dialCodeHolder = this.dialCodeHolderEl();
40513             
40514             this.trigger = this.el.select('div.flag-box',true).first();
40515             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40516             
40517             var _this = this;
40518             
40519             (function(){
40520                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40521                 _this.list.setWidth(lw);
40522             }).defer(100);
40523             
40524             this.list.on('mouseover', this.onViewOver, this);
40525             this.list.on('mousemove', this.onViewMove, this);
40526             this.inputEl().on("keyup", this.onKeyUp, this);
40527             this.inputEl().on("keypress", this.onKeyPress, this);
40528             
40529             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
40530
40531             this.view = new Roo.View(this.list, this.tpl, {
40532                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
40533             });
40534             
40535             this.view.on('click', this.onViewClick, this);
40536             this.setValue(this.defaultDialCode);
40537         },
40538         
40539         onTriggerClick : function(e)
40540         {
40541             Roo.log('trigger click');
40542             if(this.disabled){
40543                 return;
40544             }
40545             
40546             if(this.isExpanded()){
40547                 this.collapse();
40548                 this.hasFocus = false;
40549             }else {
40550                 this.store.load({});
40551                 this.hasFocus = true;
40552                 this.expand();
40553             }
40554         },
40555         
40556         isExpanded : function()
40557         {
40558             return this.list.isVisible();
40559         },
40560         
40561         collapse : function()
40562         {
40563             if(!this.isExpanded()){
40564                 return;
40565             }
40566             this.list.hide();
40567             Roo.get(document).un('mousedown', this.collapseIf, this);
40568             Roo.get(document).un('mousewheel', this.collapseIf, this);
40569             this.fireEvent('collapse', this);
40570             this.validate();
40571         },
40572         
40573         expand : function()
40574         {
40575             Roo.log('expand');
40576
40577             if(this.isExpanded() || !this.hasFocus){
40578                 return;
40579             }
40580             
40581             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
40582             this.list.setWidth(lw);
40583             
40584             this.list.show();
40585             this.restrictHeight();
40586             
40587             Roo.get(document).on('mousedown', this.collapseIf, this);
40588             Roo.get(document).on('mousewheel', this.collapseIf, this);
40589             
40590             this.fireEvent('expand', this);
40591         },
40592         
40593         restrictHeight : function()
40594         {
40595             this.list.alignTo(this.inputEl(), this.listAlign);
40596             this.list.alignTo(this.inputEl(), this.listAlign);
40597         },
40598         
40599         onViewOver : function(e, t)
40600         {
40601             if(this.inKeyMode){
40602                 return;
40603             }
40604             var item = this.view.findItemFromChild(t);
40605             
40606             if(item){
40607                 var index = this.view.indexOf(item);
40608                 this.select(index, false);
40609             }
40610         },
40611
40612         // private
40613         onViewClick : function(view, doFocus, el, e)
40614         {
40615             var index = this.view.getSelectedIndexes()[0];
40616             
40617             var r = this.store.getAt(index);
40618             
40619             if(r){
40620                 this.onSelect(r, index);
40621             }
40622             if(doFocus !== false && !this.blockFocus){
40623                 this.inputEl().focus();
40624             }
40625         },
40626         
40627         onViewMove : function(e, t)
40628         {
40629             this.inKeyMode = false;
40630         },
40631         
40632         select : function(index, scrollIntoView)
40633         {
40634             this.selectedIndex = index;
40635             this.view.select(index);
40636             if(scrollIntoView !== false){
40637                 var el = this.view.getNode(index);
40638                 if(el){
40639                     this.list.scrollChildIntoView(el, false);
40640                 }
40641             }
40642         },
40643         
40644         createList : function()
40645         {
40646             this.list = Roo.get(document.body).createChild({
40647                 tag: 'ul',
40648                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
40649                 style: 'display:none'
40650             });
40651             
40652             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40653         },
40654         
40655         collapseIf : function(e)
40656         {
40657             var in_combo  = e.within(this.el);
40658             var in_list =  e.within(this.list);
40659             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
40660             
40661             if (in_combo || in_list || is_list) {
40662                 return;
40663             }
40664             this.collapse();
40665         },
40666         
40667         onSelect : function(record, index)
40668         {
40669             if(this.fireEvent('beforeselect', this, record, index) !== false){
40670                 
40671                 this.setFlagClass(record.data.iso2);
40672                 this.setDialCode(record.data.dialCode);
40673                 this.hasFocus = false;
40674                 this.collapse();
40675                 this.fireEvent('select', this, record, index);
40676             }
40677         },
40678         
40679         flagEl : function()
40680         {
40681             var flag = this.el.select('div.flag',true).first();
40682             if(!flag){
40683                 return false;
40684             }
40685             return flag;
40686         },
40687         
40688         dialCodeHolderEl : function()
40689         {
40690             var d = this.el.select('input.dial-code-holder',true).first();
40691             if(!d){
40692                 return false;
40693             }
40694             return d;
40695         },
40696         
40697         setDialCode : function(v)
40698         {
40699             this.dialCodeHolder.dom.value = '+'+v;
40700         },
40701         
40702         setFlagClass : function(n)
40703         {
40704             this.flag.dom.className = 'flag '+n;
40705         },
40706         
40707         getValue : function()
40708         {
40709             var v = this.inputEl().getValue();
40710             if(this.dialCodeHolder) {
40711                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
40712             }
40713             return v;
40714         },
40715         
40716         setValue : function(v)
40717         {
40718             var d = this.getDialCode(v);
40719             
40720             //invalid dial code
40721             if(v.length == 0 || !d || d.length == 0) {
40722                 if(this.rendered){
40723                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40724                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40725                 }
40726                 return;
40727             }
40728             
40729             //valid dial code
40730             this.setFlagClass(this.dialCodeMapping[d].iso2);
40731             this.setDialCode(d);
40732             this.inputEl().dom.value = v.replace('+'+d,'');
40733             this.hiddenEl().dom.value = this.getValue();
40734             
40735             this.validate();
40736         },
40737         
40738         getDialCode : function(v)
40739         {
40740             v = v ||  '';
40741             
40742             if (v.length == 0) {
40743                 return this.dialCodeHolder.dom.value;
40744             }
40745             
40746             var dialCode = "";
40747             if (v.charAt(0) != "+") {
40748                 return false;
40749             }
40750             var numericChars = "";
40751             for (var i = 1; i < v.length; i++) {
40752               var c = v.charAt(i);
40753               if (!isNaN(c)) {
40754                 numericChars += c;
40755                 if (this.dialCodeMapping[numericChars]) {
40756                   dialCode = v.substr(1, i);
40757                 }
40758                 if (numericChars.length == 4) {
40759                   break;
40760                 }
40761               }
40762             }
40763             return dialCode;
40764         },
40765         
40766         reset : function()
40767         {
40768             this.setValue(this.defaultDialCode);
40769             this.validate();
40770         },
40771         
40772         hiddenEl : function()
40773         {
40774             return this.el.select('input.hidden-tel-input',true).first();
40775         },
40776         
40777         // after setting val
40778         onKeyUp : function(e){
40779             this.setValue(this.getValue());
40780         },
40781         
40782         onKeyPress : function(e){
40783             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
40784                 e.stopEvent();
40785             }
40786         }
40787         
40788 });
40789 /**
40790  * @class Roo.bootstrap.MoneyField
40791  * @extends Roo.bootstrap.ComboBox
40792  * Bootstrap MoneyField class
40793  * 
40794  * @constructor
40795  * Create a new MoneyField.
40796  * @param {Object} config Configuration options
40797  */
40798
40799 Roo.bootstrap.MoneyField = function(config) {
40800     
40801     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40802     
40803 };
40804
40805 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40806     
40807     /**
40808      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40809      */
40810     allowDecimals : true,
40811     /**
40812      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40813      */
40814     decimalSeparator : ".",
40815     /**
40816      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40817      */
40818     decimalPrecision : 0,
40819     /**
40820      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40821      */
40822     allowNegative : true,
40823     /**
40824      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40825      */
40826     allowZero: true,
40827     /**
40828      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40829      */
40830     minValue : Number.NEGATIVE_INFINITY,
40831     /**
40832      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40833      */
40834     maxValue : Number.MAX_VALUE,
40835     /**
40836      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40837      */
40838     minText : "The minimum value for this field is {0}",
40839     /**
40840      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40841      */
40842     maxText : "The maximum value for this field is {0}",
40843     /**
40844      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40845      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40846      */
40847     nanText : "{0} is not a valid number",
40848     /**
40849      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40850      */
40851     castInt : true,
40852     /**
40853      * @cfg {String} defaults currency of the MoneyField
40854      * value should be in lkey
40855      */
40856     defaultCurrency : false,
40857     /**
40858      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40859      */
40860     thousandsDelimiter : false,
40861     /**
40862      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
40863      */
40864     max_length: false,
40865     
40866     inputlg : 9,
40867     inputmd : 9,
40868     inputsm : 9,
40869     inputxs : 6,
40870     
40871     store : false,
40872     
40873     getAutoCreate : function()
40874     {
40875         var align = this.labelAlign || this.parentLabelAlign();
40876         
40877         var id = Roo.id();
40878
40879         var cfg = {
40880             cls: 'form-group',
40881             cn: []
40882         };
40883
40884         var input =  {
40885             tag: 'input',
40886             id : id,
40887             cls : 'form-control roo-money-amount-input',
40888             autocomplete: 'new-password'
40889         };
40890         
40891         var hiddenInput = {
40892             tag: 'input',
40893             type: 'hidden',
40894             id: Roo.id(),
40895             cls: 'hidden-number-input'
40896         };
40897         
40898         if(this.max_length) {
40899             input.maxlength = this.max_length; 
40900         }
40901         
40902         if (this.name) {
40903             hiddenInput.name = this.name;
40904         }
40905
40906         if (this.disabled) {
40907             input.disabled = true;
40908         }
40909
40910         var clg = 12 - this.inputlg;
40911         var cmd = 12 - this.inputmd;
40912         var csm = 12 - this.inputsm;
40913         var cxs = 12 - this.inputxs;
40914         
40915         var container = {
40916             tag : 'div',
40917             cls : 'row roo-money-field',
40918             cn : [
40919                 {
40920                     tag : 'div',
40921                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40922                     cn : [
40923                         {
40924                             tag : 'div',
40925                             cls: 'roo-select2-container input-group',
40926                             cn: [
40927                                 {
40928                                     tag : 'input',
40929                                     cls : 'form-control roo-money-currency-input',
40930                                     autocomplete: 'new-password',
40931                                     readOnly : 1,
40932                                     name : this.currencyName
40933                                 },
40934                                 {
40935                                     tag :'span',
40936                                     cls : 'input-group-addon',
40937                                     cn : [
40938                                         {
40939                                             tag: 'span',
40940                                             cls: 'caret'
40941                                         }
40942                                     ]
40943                                 }
40944                             ]
40945                         }
40946                     ]
40947                 },
40948                 {
40949                     tag : 'div',
40950                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40951                     cn : [
40952                         {
40953                             tag: 'div',
40954                             cls: this.hasFeedback ? 'has-feedback' : '',
40955                             cn: [
40956                                 input
40957                             ]
40958                         }
40959                     ]
40960                 }
40961             ]
40962             
40963         };
40964         
40965         if (this.fieldLabel.length) {
40966             var indicator = {
40967                 tag: 'i',
40968                 tooltip: 'This field is required'
40969             };
40970
40971             var label = {
40972                 tag: 'label',
40973                 'for':  id,
40974                 cls: 'control-label',
40975                 cn: []
40976             };
40977
40978             var label_text = {
40979                 tag: 'span',
40980                 html: this.fieldLabel
40981             };
40982
40983             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40984             label.cn = [
40985                 indicator,
40986                 label_text
40987             ];
40988
40989             if(this.indicatorpos == 'right') {
40990                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40991                 label.cn = [
40992                     label_text,
40993                     indicator
40994                 ];
40995             }
40996
40997             if(align == 'left') {
40998                 container = {
40999                     tag: 'div',
41000                     cn: [
41001                         container
41002                     ]
41003                 };
41004
41005                 if(this.labelWidth > 12){
41006                     label.style = "width: " + this.labelWidth + 'px';
41007                 }
41008                 if(this.labelWidth < 13 && this.labelmd == 0){
41009                     this.labelmd = this.labelWidth;
41010                 }
41011                 if(this.labellg > 0){
41012                     label.cls += ' col-lg-' + this.labellg;
41013                     input.cls += ' col-lg-' + (12 - this.labellg);
41014                 }
41015                 if(this.labelmd > 0){
41016                     label.cls += ' col-md-' + this.labelmd;
41017                     container.cls += ' col-md-' + (12 - this.labelmd);
41018                 }
41019                 if(this.labelsm > 0){
41020                     label.cls += ' col-sm-' + this.labelsm;
41021                     container.cls += ' col-sm-' + (12 - this.labelsm);
41022                 }
41023                 if(this.labelxs > 0){
41024                     label.cls += ' col-xs-' + this.labelxs;
41025                     container.cls += ' col-xs-' + (12 - this.labelxs);
41026                 }
41027             }
41028         }
41029
41030         cfg.cn = [
41031             label,
41032             container,
41033             hiddenInput
41034         ];
41035         
41036         var settings = this;
41037
41038         ['xs','sm','md','lg'].map(function(size){
41039             if (settings[size]) {
41040                 cfg.cls += ' col-' + size + '-' + settings[size];
41041             }
41042         });
41043         
41044         return cfg;
41045     },
41046     
41047     initEvents : function()
41048     {
41049         this.indicator = this.indicatorEl();
41050         
41051         this.initCurrencyEvent();
41052         
41053         this.initNumberEvent();
41054     },
41055     
41056     initCurrencyEvent : function()
41057     {
41058         if (!this.store) {
41059             throw "can not find store for combo";
41060         }
41061         
41062         this.store = Roo.factory(this.store, Roo.data);
41063         this.store.parent = this;
41064         
41065         this.createList();
41066         
41067         this.triggerEl = this.el.select('.input-group-addon', true).first();
41068         
41069         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
41070         
41071         var _this = this;
41072         
41073         (function(){
41074             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
41075             _this.list.setWidth(lw);
41076         }).defer(100);
41077         
41078         this.list.on('mouseover', this.onViewOver, this);
41079         this.list.on('mousemove', this.onViewMove, this);
41080         this.list.on('scroll', this.onViewScroll, this);
41081         
41082         if(!this.tpl){
41083             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
41084         }
41085         
41086         this.view = new Roo.View(this.list, this.tpl, {
41087             singleSelect:true, store: this.store, selectedClass: this.selectedClass
41088         });
41089         
41090         this.view.on('click', this.onViewClick, this);
41091         
41092         this.store.on('beforeload', this.onBeforeLoad, this);
41093         this.store.on('load', this.onLoad, this);
41094         this.store.on('loadexception', this.onLoadException, this);
41095         
41096         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
41097             "up" : function(e){
41098                 this.inKeyMode = true;
41099                 this.selectPrev();
41100             },
41101
41102             "down" : function(e){
41103                 if(!this.isExpanded()){
41104                     this.onTriggerClick();
41105                 }else{
41106                     this.inKeyMode = true;
41107                     this.selectNext();
41108                 }
41109             },
41110
41111             "enter" : function(e){
41112                 this.collapse();
41113                 
41114                 if(this.fireEvent("specialkey", this, e)){
41115                     this.onViewClick(false);
41116                 }
41117                 
41118                 return true;
41119             },
41120
41121             "esc" : function(e){
41122                 this.collapse();
41123             },
41124
41125             "tab" : function(e){
41126                 this.collapse();
41127                 
41128                 if(this.fireEvent("specialkey", this, e)){
41129                     this.onViewClick(false);
41130                 }
41131                 
41132                 return true;
41133             },
41134
41135             scope : this,
41136
41137             doRelay : function(foo, bar, hname){
41138                 if(hname == 'down' || this.scope.isExpanded()){
41139                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41140                 }
41141                 return true;
41142             },
41143
41144             forceKeyDown: true
41145         });
41146         
41147         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
41148         
41149     },
41150     
41151     initNumberEvent : function(e)
41152     {
41153         this.inputEl().on("keydown" , this.fireKey,  this);
41154         this.inputEl().on("focus", this.onFocus,  this);
41155         this.inputEl().on("blur", this.onBlur,  this);
41156         
41157         this.inputEl().relayEvent('keyup', this);
41158         
41159         if(this.indicator){
41160             this.indicator.addClass('invisible');
41161         }
41162  
41163         this.originalValue = this.getValue();
41164         
41165         if(this.validationEvent == 'keyup'){
41166             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41167             this.inputEl().on('keyup', this.filterValidation, this);
41168         }
41169         else if(this.validationEvent !== false){
41170             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41171         }
41172         
41173         if(this.selectOnFocus){
41174             this.on("focus", this.preFocus, this);
41175             
41176         }
41177         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41178             this.inputEl().on("keypress", this.filterKeys, this);
41179         } else {
41180             this.inputEl().relayEvent('keypress', this);
41181         }
41182         
41183         var allowed = "0123456789";
41184         
41185         if(this.allowDecimals){
41186             allowed += this.decimalSeparator;
41187         }
41188         
41189         if(this.allowNegative){
41190             allowed += "-";
41191         }
41192         
41193         if(this.thousandsDelimiter) {
41194             allowed += ",";
41195         }
41196         
41197         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41198         
41199         var keyPress = function(e){
41200             
41201             var k = e.getKey();
41202             
41203             var c = e.getCharCode();
41204             
41205             if(
41206                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41207                     allowed.indexOf(String.fromCharCode(c)) === -1
41208             ){
41209                 e.stopEvent();
41210                 return;
41211             }
41212             
41213             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41214                 return;
41215             }
41216             
41217             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41218                 e.stopEvent();
41219             }
41220         };
41221         
41222         this.inputEl().on("keypress", keyPress, this);
41223         
41224     },
41225     
41226     onTriggerClick : function(e)
41227     {   
41228         if(this.disabled){
41229             return;
41230         }
41231         
41232         this.page = 0;
41233         this.loadNext = false;
41234         
41235         if(this.isExpanded()){
41236             this.collapse();
41237             return;
41238         }
41239         
41240         this.hasFocus = true;
41241         
41242         if(this.triggerAction == 'all') {
41243             this.doQuery(this.allQuery, true);
41244             return;
41245         }
41246         
41247         this.doQuery(this.getRawValue());
41248     },
41249     
41250     getCurrency : function()
41251     {   
41252         var v = this.currencyEl().getValue();
41253         
41254         return v;
41255     },
41256     
41257     restrictHeight : function()
41258     {
41259         this.list.alignTo(this.currencyEl(), this.listAlign);
41260         this.list.alignTo(this.currencyEl(), this.listAlign);
41261     },
41262     
41263     onViewClick : function(view, doFocus, el, e)
41264     {
41265         var index = this.view.getSelectedIndexes()[0];
41266         
41267         var r = this.store.getAt(index);
41268         
41269         if(r){
41270             this.onSelect(r, index);
41271         }
41272     },
41273     
41274     onSelect : function(record, index){
41275         
41276         if(this.fireEvent('beforeselect', this, record, index) !== false){
41277         
41278             this.setFromCurrencyData(index > -1 ? record.data : false);
41279             
41280             this.collapse();
41281             
41282             this.fireEvent('select', this, record, index);
41283         }
41284     },
41285     
41286     setFromCurrencyData : function(o)
41287     {
41288         var currency = '';
41289         
41290         this.lastCurrency = o;
41291         
41292         if (this.currencyField) {
41293             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
41294         } else {
41295             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
41296         }
41297         
41298         this.lastSelectionText = currency;
41299         
41300         //setting default currency
41301         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
41302             this.setCurrency(this.defaultCurrency);
41303             return;
41304         }
41305         
41306         this.setCurrency(currency);
41307     },
41308     
41309     setFromData : function(o)
41310     {
41311         var c = {};
41312         
41313         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
41314         
41315         this.setFromCurrencyData(c);
41316         
41317         var value = '';
41318         
41319         if (this.name) {
41320             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
41321         } else {
41322             Roo.log('no value set for '+ (this.name ? this.name : this.id));
41323         }
41324         
41325         this.setValue(value);
41326         
41327     },
41328     
41329     setCurrency : function(v)
41330     {   
41331         this.currencyValue = v;
41332         
41333         if(this.rendered){
41334             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
41335             this.validate();
41336         }
41337     },
41338     
41339     setValue : function(v)
41340     {
41341         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41342         
41343         this.value = v;
41344         
41345         if(this.rendered){
41346             
41347             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41348             
41349             this.inputEl().dom.value = (v == '') ? '' :
41350                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41351             
41352             if(!this.allowZero && v === '0') {
41353                 this.hiddenEl().dom.value = '';
41354                 this.inputEl().dom.value = '';
41355             }
41356             
41357             this.validate();
41358         }
41359     },
41360     
41361     getRawValue : function()
41362     {
41363         var v = this.inputEl().getValue();
41364         
41365         return v;
41366     },
41367     
41368     getValue : function()
41369     {
41370         return this.fixPrecision(this.parseValue(this.getRawValue()));
41371     },
41372     
41373     parseValue : function(value)
41374     {
41375         if(this.thousandsDelimiter) {
41376             value += "";
41377             r = new RegExp(",", "g");
41378             value = value.replace(r, "");
41379         }
41380         
41381         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41382         return isNaN(value) ? '' : value;
41383         
41384     },
41385     
41386     fixPrecision : function(value)
41387     {
41388         if(this.thousandsDelimiter) {
41389             value += "";
41390             r = new RegExp(",", "g");
41391             value = value.replace(r, "");
41392         }
41393         
41394         var nan = isNaN(value);
41395         
41396         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41397             return nan ? '' : value;
41398         }
41399         return parseFloat(value).toFixed(this.decimalPrecision);
41400     },
41401     
41402     decimalPrecisionFcn : function(v)
41403     {
41404         return Math.floor(v);
41405     },
41406     
41407     validateValue : function(value)
41408     {
41409         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
41410             return false;
41411         }
41412         
41413         var num = this.parseValue(value);
41414         
41415         if(isNaN(num)){
41416             this.markInvalid(String.format(this.nanText, value));
41417             return false;
41418         }
41419         
41420         if(num < this.minValue){
41421             this.markInvalid(String.format(this.minText, this.minValue));
41422             return false;
41423         }
41424         
41425         if(num > this.maxValue){
41426             this.markInvalid(String.format(this.maxText, this.maxValue));
41427             return false;
41428         }
41429         
41430         return true;
41431     },
41432     
41433     validate : function()
41434     {
41435         if(this.disabled || this.allowBlank){
41436             this.markValid();
41437             return true;
41438         }
41439         
41440         var currency = this.getCurrency();
41441         
41442         if(this.validateValue(this.getRawValue()) && currency.length){
41443             this.markValid();
41444             return true;
41445         }
41446         
41447         this.markInvalid();
41448         return false;
41449     },
41450     
41451     getName: function()
41452     {
41453         return this.name;
41454     },
41455     
41456     beforeBlur : function()
41457     {
41458         if(!this.castInt){
41459             return;
41460         }
41461         
41462         var v = this.parseValue(this.getRawValue());
41463         
41464         if(v || v == 0){
41465             this.setValue(v);
41466         }
41467     },
41468     
41469     onBlur : function()
41470     {
41471         this.beforeBlur();
41472         
41473         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
41474             //this.el.removeClass(this.focusClass);
41475         }
41476         
41477         this.hasFocus = false;
41478         
41479         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
41480             this.validate();
41481         }
41482         
41483         var v = this.getValue();
41484         
41485         if(String(v) !== String(this.startValue)){
41486             this.fireEvent('change', this, v, this.startValue);
41487         }
41488         
41489         this.fireEvent("blur", this);
41490     },
41491     
41492     inputEl : function()
41493     {
41494         return this.el.select('.roo-money-amount-input', true).first();
41495     },
41496     
41497     currencyEl : function()
41498     {
41499         return this.el.select('.roo-money-currency-input', true).first();
41500     },
41501     
41502     hiddenEl : function()
41503     {
41504         return this.el.select('input.hidden-number-input',true).first();
41505     }
41506     
41507 });/**
41508 *    This script refer to:
41509 *    Title: Signature Pad
41510 *    Author: szimek
41511 *    Availability: https://github.com/szimek/signature_pad
41512 **/
41513
41514 /**
41515  * @class Roo.bootstrap.BezierSignature
41516  * @extends Roo.bootstrap.Component
41517  * Bootstrap BezierSignature class
41518  * 
41519  * @constructor
41520  * Create a new BezierSignature
41521  * @param {Object} config The config object
41522  */
41523
41524 Roo.bootstrap.BezierSignature = function(config){
41525     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
41526     this.addEvents({
41527         "resize" : true
41528     });
41529 };
41530
41531 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,  {
41532     
41533     curve_data: [],
41534     
41535     is_empty: true,
41536     
41537     mouse_btn_down: true,
41538     
41539     /**
41540      * @cfg(int) canvas height
41541      */
41542     canvas_height: '200px',
41543     
41544     /**
41545      * @cfg(float or function) Radius of a single dot.
41546      */ 
41547     dot_size: false,
41548     
41549     /**
41550      * @cfg(float) Minimum width of a line. Defaults to 0.5.
41551      */
41552     min_width: 0.5,
41553     
41554     /**
41555      * @cfg(float) Maximum width of a line. Defaults to 2.5.
41556      */
41557     max_width: 2.5,
41558     
41559     /**
41560      * @cfg(integer) Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
41561      */
41562     throttle: 16,
41563     
41564     /**
41565      * @cfg(integer) Add the next point only if the previous one is farther than x pixels. Defaults to 5.
41566      */
41567     min_distance: 5,
41568     
41569     /**
41570      * @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.
41571      */
41572     bg_color: 'rgba(0, 0, 0, 0)',
41573     
41574     /**
41575      * @cfg(string) Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
41576      */
41577     dot_color: 'black',
41578     
41579     /**
41580      * @cfg(float) Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
41581      */
41582     velocity_filter_weight: 0.7,
41583     
41584     /**
41585      * @cfg(function) Callback when stroke begin.
41586      */
41587     onBegin: false,
41588     
41589     /**
41590      * @cfg(function) Callback when stroke end.
41591      */
41592     onEnd: false,
41593     
41594     getAutoCreate : function()
41595     {
41596         var cls = 'roo-signature column';
41597         
41598         if(this.cls){
41599             cls += ' ' + this.cls;
41600         }
41601         
41602         var col_sizes = [
41603             'lg',
41604             'md',
41605             'sm',
41606             'xs'
41607         ];
41608         
41609         for(var i = 0; i < col_sizes.length; i++) {
41610             if(this[col_sizes[i]]) {
41611                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
41612             }
41613         }
41614         
41615         var cfg = {
41616             tag: 'div',
41617             cls: cls,
41618             cn: [
41619                 {
41620                     tag: 'div',
41621                     cls: 'roo-signature-body',
41622                     cn: [
41623                         {
41624                             tag: 'canvas',
41625                             cls: 'roo-signature-body-canvas',
41626                             height: this.canvas_height,
41627                             width: this.canvas_width
41628                         }
41629                     ]
41630                 },
41631                 {
41632                     tag: 'input',
41633                     type: 'file',
41634                     style: 'display: none'
41635                 }
41636             ]
41637         };
41638         
41639         return cfg;
41640     },
41641     
41642     initEvents: function() 
41643     {
41644         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
41645         
41646         var canvas = this.canvasEl();
41647         
41648         // mouse && touch event swapping...
41649         canvas.dom.style.touchAction = 'none';
41650         canvas.dom.style.msTouchAction = 'none';
41651         
41652         this.mouse_btn_down = false;
41653         canvas.on('mousedown', this._handleMouseDown, this);
41654         canvas.on('mousemove', this._handleMouseMove, this);
41655         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
41656         
41657         if (window.PointerEvent) {
41658             canvas.on('pointerdown', this._handleMouseDown, this);
41659             canvas.on('pointermove', this._handleMouseMove, this);
41660             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
41661         }
41662         
41663         if ('ontouchstart' in window) {
41664             canvas.on('touchstart', this._handleTouchStart, this);
41665             canvas.on('touchmove', this._handleTouchMove, this);
41666             canvas.on('touchend', this._handleTouchEnd, this);
41667         }
41668         
41669         Roo.EventManager.onWindowResize(this.resize, this, true);
41670         
41671         // file input event
41672         this.fileEl().on('change', this.uploadImage, this);
41673         
41674         this.clear();
41675         
41676         this.resize();
41677     },
41678     
41679     resize: function(){
41680         
41681         var canvas = this.canvasEl().dom;
41682         var ctx = this.canvasElCtx();
41683         var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
41684         
41685         // setting canvas width will clean img data
41686         canvas.width = 0;
41687         
41688         var style = window.getComputedStyle ? 
41689             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
41690             
41691         var padding_left = parseInt(style.paddingLeft) || 0;
41692         var padding_right = parseInt(style.paddingRight) || 0;
41693         
41694         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
41695         
41696         ctx.putImageData(img_data, 0, 0);
41697     },
41698     
41699     _handleMouseDown: function(e)
41700     {
41701         if (e.browserEvent.which === 1) {
41702             this.mouse_btn_down = true;
41703             this.strokeBegin(e);
41704         }
41705     },
41706     
41707     _handleMouseMove: function (e)
41708     {
41709         if (this.mouse_btn_down) {
41710             this.strokeMoveUpdate(e);
41711         }
41712     },
41713     
41714     _handleMouseUp: function (e)
41715     {
41716         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
41717             this.mouse_btn_down = false;
41718             this.strokeEnd(e);
41719         }
41720     },
41721     
41722     _handleTouchStart: function (e) {
41723         
41724         e.preventDefault();
41725         if (e.browserEvent.targetTouches.length === 1) {
41726             // var touch = e.browserEvent.changedTouches[0];
41727             // this.strokeBegin(touch);
41728             
41729              this.strokeBegin(e); // assume e catching the correct xy...
41730         }
41731     },
41732     
41733     _handleTouchMove: function (e) {
41734         e.preventDefault();
41735         // var touch = event.targetTouches[0];
41736         // _this._strokeMoveUpdate(touch);
41737         this.strokeMoveUpdate(e);
41738     },
41739     
41740     _handleTouchEnd: function (e) {
41741         var wasCanvasTouched = e.target === this.canvasEl().dom;
41742         if (wasCanvasTouched) {
41743             e.preventDefault();
41744             // var touch = event.changedTouches[0];
41745             // _this._strokeEnd(touch);
41746             this.strokeEnd(e);
41747         }
41748     },
41749     
41750     reset: function () {
41751         this._lastPoints = [];
41752         this._lastVelocity = 0;
41753         this._lastWidth = (this.min_width + this.max_width) / 2;
41754         this.canvasElCtx().fillStyle = this.dot_color;
41755     },
41756     
41757     strokeMoveUpdate: function(e)
41758     {
41759         this.strokeUpdate(e);
41760         
41761         if (this.throttle) {
41762             this.throttle(this.strokeUpdate, this.throttle);
41763         }
41764         else {
41765             this.strokeUpdate(e);
41766         }
41767     },
41768     
41769     strokeBegin: function(e)
41770     {
41771         var newPointGroup = {
41772             color: this.dot_color,
41773             points: []
41774         };
41775         
41776         if (typeof this.onBegin === 'function') {
41777             this.onBegin(e);
41778         }
41779         
41780         this.curve_data.push(newPointGroup);
41781         this.reset();
41782         this.strokeUpdate(e);
41783     },
41784     
41785     strokeUpdate: function(e)
41786     {
41787         var rect = this.canvasEl().dom.getBoundingClientRect();
41788         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
41789         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
41790         var lastPoints = lastPointGroup.points;
41791         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
41792         var isLastPointTooClose = lastPoint
41793             ? point.distanceTo(lastPoint) <= this.min_distance
41794             : false;
41795         var color = lastPointGroup.color;
41796         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
41797             var curve = this.addPoint(point);
41798             if (!lastPoint) {
41799                 this.drawDot({color: color, point: point});
41800             }
41801             else if (curve) {
41802                 this.drawCurve({color: color, curve: curve});
41803             }
41804             lastPoints.push({
41805                 time: point.time,
41806                 x: point.x,
41807                 y: point.y
41808             });
41809         }
41810     },
41811     
41812     strokeEnd: function(e)
41813     {
41814         this.strokeUpdate(e);
41815         if (typeof this.onEnd === 'function') {
41816             this.onEnd(e);
41817         }
41818     },
41819     
41820     addPoint:  function (point) {
41821         var _lastPoints = this._lastPoints;
41822         _lastPoints.push(point);
41823         if (_lastPoints.length > 2) {
41824             if (_lastPoints.length === 3) {
41825                 _lastPoints.unshift(_lastPoints[0]);
41826             }
41827             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
41828             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
41829             _lastPoints.shift();
41830             return curve;
41831         }
41832         return null;
41833     },
41834     
41835     calculateCurveWidths: function (startPoint, endPoint) {
41836         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
41837             (1 - this.velocity_filter_weight) * this._lastVelocity;
41838
41839         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
41840         var widths = {
41841             end: newWidth,
41842             start: this._lastWidth
41843         };
41844         
41845         this._lastVelocity = velocity;
41846         this._lastWidth = newWidth;
41847         return widths;
41848     },
41849     
41850     drawDot: function (_a) {
41851         var color = _a.color, point = _a.point;
41852         var ctx = this.canvasElCtx();
41853         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
41854         ctx.beginPath();
41855         this.drawCurveSegment(point.x, point.y, width);
41856         ctx.closePath();
41857         ctx.fillStyle = color;
41858         ctx.fill();
41859     },
41860     
41861     drawCurve: function (_a) {
41862         var color = _a.color, curve = _a.curve;
41863         var ctx = this.canvasElCtx();
41864         var widthDelta = curve.endWidth - curve.startWidth;
41865         var drawSteps = Math.floor(curve.length()) * 2;
41866         ctx.beginPath();
41867         ctx.fillStyle = color;
41868         for (var i = 0; i < drawSteps; i += 1) {
41869         var t = i / drawSteps;
41870         var tt = t * t;
41871         var ttt = tt * t;
41872         var u = 1 - t;
41873         var uu = u * u;
41874         var uuu = uu * u;
41875         var x = uuu * curve.startPoint.x;
41876         x += 3 * uu * t * curve.control1.x;
41877         x += 3 * u * tt * curve.control2.x;
41878         x += ttt * curve.endPoint.x;
41879         var y = uuu * curve.startPoint.y;
41880         y += 3 * uu * t * curve.control1.y;
41881         y += 3 * u * tt * curve.control2.y;
41882         y += ttt * curve.endPoint.y;
41883         var width = curve.startWidth + ttt * widthDelta;
41884         this.drawCurveSegment(x, y, width);
41885         }
41886         ctx.closePath();
41887         ctx.fill();
41888     },
41889     
41890     drawCurveSegment: function (x, y, width) {
41891         var ctx = this.canvasElCtx();
41892         ctx.moveTo(x, y);
41893         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
41894         this.is_empty = false;
41895     },
41896     
41897     clear: function()
41898     {
41899         var ctx = this.canvasElCtx();
41900         var canvas = this.canvasEl().dom;
41901         ctx.fillStyle = this.bg_color;
41902         ctx.clearRect(0, 0, canvas.width, canvas.height);
41903         ctx.fillRect(0, 0, canvas.width, canvas.height);
41904         this.curve_data = [];
41905         this.reset();
41906         this.is_empty = true;
41907     },
41908     
41909     fileEl: function()
41910     {
41911         return  this.el.select('input',true).first();
41912     },
41913     
41914     canvasEl: function()
41915     {
41916         return this.el.select('canvas',true).first();
41917     },
41918     
41919     canvasElCtx: function()
41920     {
41921         return this.el.select('canvas',true).first().dom.getContext('2d');
41922     },
41923     
41924     getImage: function(type)
41925     {
41926         if(this.is_empty) {
41927             return false;
41928         }
41929         
41930         // encryption ?
41931         return this.canvasEl().dom.toDataURL('image/'+type, 1);
41932     },
41933     
41934     drawFromImage: function(img_src)
41935     {
41936         var img = new Image();
41937         
41938         img.onload = function(){
41939             this.canvasElCtx().drawImage(img, 0, 0);
41940         }.bind(this);
41941         
41942         img.src = img_src;
41943         
41944         this.is_empty = false;
41945     },
41946     
41947     selectImage: function()
41948     {
41949         this.fileEl().dom.click();
41950     },
41951     
41952     uploadImage: function(e)
41953     {
41954         var reader = new FileReader();
41955         
41956         reader.onload = function(e){
41957             var img = new Image();
41958             img.onload = function(){
41959                 this.reset();
41960                 this.canvasElCtx().drawImage(img, 0, 0);
41961             }.bind(this);
41962             img.src = e.target.result;
41963         }.bind(this);
41964         
41965         reader.readAsDataURL(e.target.files[0]);
41966     },
41967     
41968     // Bezier Point Constructor
41969     Point: (function () {
41970         function Point(x, y, time) {
41971             this.x = x;
41972             this.y = y;
41973             this.time = time || Date.now();
41974         }
41975         Point.prototype.distanceTo = function (start) {
41976             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
41977         };
41978         Point.prototype.equals = function (other) {
41979             return this.x === other.x && this.y === other.y && this.time === other.time;
41980         };
41981         Point.prototype.velocityFrom = function (start) {
41982             return this.time !== start.time
41983             ? this.distanceTo(start) / (this.time - start.time)
41984             : 0;
41985         };
41986         return Point;
41987     }()),
41988     
41989     
41990     // Bezier Constructor
41991     Bezier: (function () {
41992         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
41993             this.startPoint = startPoint;
41994             this.control2 = control2;
41995             this.control1 = control1;
41996             this.endPoint = endPoint;
41997             this.startWidth = startWidth;
41998             this.endWidth = endWidth;
41999         }
42000         Bezier.fromPoints = function (points, widths, scope) {
42001             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
42002             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
42003             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
42004         };
42005         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
42006             var dx1 = s1.x - s2.x;
42007             var dy1 = s1.y - s2.y;
42008             var dx2 = s2.x - s3.x;
42009             var dy2 = s2.y - s3.y;
42010             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
42011             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
42012             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
42013             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
42014             var dxm = m1.x - m2.x;
42015             var dym = m1.y - m2.y;
42016             var k = l2 / (l1 + l2);
42017             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
42018             var tx = s2.x - cm.x;
42019             var ty = s2.y - cm.y;
42020             return {
42021                 c1: new scope.Point(m1.x + tx, m1.y + ty),
42022                 c2: new scope.Point(m2.x + tx, m2.y + ty)
42023             };
42024         };
42025         Bezier.prototype.length = function () {
42026             var steps = 10;
42027             var length = 0;
42028             var px;
42029             var py;
42030             for (var i = 0; i <= steps; i += 1) {
42031                 var t = i / steps;
42032                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
42033                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
42034                 if (i > 0) {
42035                     var xdiff = cx - px;
42036                     var ydiff = cy - py;
42037                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
42038                 }
42039                 px = cx;
42040                 py = cy;
42041             }
42042             return length;
42043         };
42044         Bezier.prototype.point = function (t, start, c1, c2, end) {
42045             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
42046             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
42047             + (3.0 * c2 * (1.0 - t) * t * t)
42048             + (end * t * t * t);
42049         };
42050         return Bezier;
42051     }()),
42052     
42053     throttle: function(fn, wait) {
42054       if (wait === void 0) { wait = 250; }
42055       var previous = 0;
42056       var timeout = null;
42057       var result;
42058       var storedContext;
42059       var storedArgs;
42060       var later = function () {
42061           previous = Date.now();
42062           timeout = null;
42063           result = fn.apply(storedContext, storedArgs);
42064           if (!timeout) {
42065               storedContext = null;
42066               storedArgs = [];
42067           }
42068       };
42069       return function wrapper() {
42070           var args = [];
42071           for (var _i = 0; _i < arguments.length; _i++) {
42072               args[_i] = arguments[_i];
42073           }
42074           var now = Date.now();
42075           var remaining = wait - (now - previous);
42076           storedContext = this;
42077           storedArgs = args;
42078           if (remaining <= 0 || remaining > wait) {
42079               if (timeout) {
42080                   clearTimeout(timeout);
42081                   timeout = null;
42082               }
42083               previous = now;
42084               result = fn.apply(storedContext, storedArgs);
42085               if (!timeout) {
42086                   storedContext = null;
42087                   storedArgs = [];
42088               }
42089           }
42090           else if (!timeout) {
42091               timeout = window.setTimeout(later, remaining);
42092           }
42093           return result;
42094       };
42095   }
42096   
42097 });
42098
42099  
42100
42101